[Cluster-devel] conga ./.cvsignore ./Makefile ./clustermon.spe ...

rmccabe at sourceware.org rmccabe at sourceware.org
Thu Apr 12 17:19:56 UTC 2007


CVSROOT:	/cvs/cluster
Module name:	conga
Branch: 	RHEL50_Z_branchpoint
Changes by:	rmccabe at sourceware.org	2007-04-12 18:19:36

Modified files:
	.              : .cvsignore Makefile clustermon.spec.in.in 
	                 conga.spec.in.in download_files 
	luci           : TODO load_site.py pack.py 
	luci/cluster   : fence_device.js form-chooser form-macros 
	                 index_html resource-form-macros 
	                 resource_form_handlers.js 
	                 validate_config_general.js 
	                 validate_config_qdisk.js validate_fence.js 
	luci/conga_ssl : SSLClient.cpp conga_ssl_lib.cpp setup.py 
	luci/homebase  : form-macros homebase_common.js 
	                 luci_homebase.css validate_cluster_add.js 
	luci/plone-custom: footer 
	luci/site/luci/Extensions: Cluster.py Clusterfs.py 
	                           FailoverDomain.py FenceHandler.py 
	                           Fs.py Ip.py ModelBuilder.py 
	                           NFSClient.py NFSExport.py Netfs.py 
	                           Samba.py Script.py StorageReport.py 
	                           TagObject.py clui_constants.py 
	                           cluster_adapters.py 
	                           conga_constants.py 
	                           homebase_adapters.py ricci_bridge.py 
	                           ricci_communicator.py 
	luci/site/luci/var: Data.fs 
	luci/utils     : luci_admin luci_cleanup luci_manage 
	make           : version.in 
	ricci          : ricci.spec.in.in 
	ricci/common   : ClientSocket.cpp Makefile Socket.cpp Time.cpp 
	                 XML.cpp executils.cpp 
	ricci/docs     : cluster_api.html modules.html rpm_api.html 
	                 service_api.html storage_api.html 
	ricci/include  : Socket.h 
	ricci/modules/cluster: ClusterModule.cpp ClusterStatus.cpp 
	                       Clusvcadm.cpp Clusvcadm.h 
	ricci/modules/cluster/clumon/src/daemon: Monitor.cpp Monitor.h 
	ricci/modules/log: LogParser.cpp 
	ricci/modules/rpm: PackageHandler.cpp 
	ricci/modules/service: ServiceManager.cpp ServiceManager.h 
	                       ServiceModule.cpp 
	ricci/modules/storage: ClusterNotQuorateError.h ClvmdError.h 
	                       GFS1.cpp GFS2.cpp LV.cpp LVM.cpp PV.cpp 
	                       VG.cpp defines.h parted_wrapper.cpp 
	ricci/ricci    : Ricci.cpp 
Added files:
	.              : Plone-2.5.2-1_CMFPlone.patch 
	luci/cluster   : validate_config_gulm.js validate_create_gulm.js 
	                 validate_fdom.js 
	luci/site/luci/Extensions: Apache.py FenceXVMd.py LVM.py 
	                           MySQL.py OpenLDAP.py Postgres8.py 
	                           Tomcat5.py Totem.py 
	luci/test      : CGA_0160_Add_User.py 
	                 CGA_0170_Online_Documentation_Portlet.py 
	                 CGA_0200_Create_cluster.py cleaner.py 
	                 congaDemoTests.py conga_Helpers.py 
	                 conga_suite.py loggerObject.py tests_README.txt 
	ricci/common   : Network.cpp 
	ricci/include  : Network.h 
	ricci/modules/storage: ClusterNotRunningError.h 
	                       LVMClusterLockingError.h 
	ricci/test_suite: README SSLClient_send_to_ricci cacert.config 
	                  generate_certs.sh send_to_ricci 
	ricci/test_suite/storage: report.xml 
Removed files:
	.              : Plone-2.5_CMFPlone.patch 
	luci/site/luci/Extensions: CommandError.py CommandHandler.py 
	                           MessageLibrary.py ValidationError.py 
	                           Xenvm.py 

Log message:
	Sync up the branch for Z with the RHEL5 branch
	Will add Resolves and Related lines to the changelog before building

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/Plone-2.5.2-1_CMFPlone.patch.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/.cvsignore.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.11.2.1&r2=1.11.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/clustermon.spec.in.in.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.18.2.12&r2=1.18.2.12.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/conga.spec.in.in.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.45.2.24&r2=1.45.2.24.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/download_files.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/Plone-2.5_CMFPlone.patch.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=NONE
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/TODO.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/load_site.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.14.2.1&r2=1.14.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/pack.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4.2.1&r2=1.4.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_config_gulm.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_create_gulm.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_fdom.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/fence_device.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2.2.4&r2=1.2.2.4.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/form-chooser.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.12.2.3&r2=1.12.2.3.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/form-macros.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.90.2.18&r2=1.90.2.18.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/index_html.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.20.2.6&r2=1.20.2.6.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/resource-form-macros.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.21.2.3&r2=1.21.2.3.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/resource_form_handlers.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.20.2.3&r2=1.20.2.3.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_config_general.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_config_qdisk.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4.2.1&r2=1.4.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_fence.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/SSLClient.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1.2.2&r2=1.1.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/conga_ssl_lib.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1.2.2&r2=1.1.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/setup.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1.2.1&r2=1.1.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/form-macros.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.44.2.7&r2=1.44.2.7.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/homebase_common.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.13.2.2&r2=1.13.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/luci_homebase.css.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.28.2.4&r2=1.28.2.4.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/validate_cluster_add.js.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4.2.1&r2=1.4.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/plone-custom/footer.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2.2.1&r2=1.2.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Apache.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/FenceXVMd.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/LVM.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/MySQL.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/OpenLDAP.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Postgres8.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Tomcat5.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Totem.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Cluster.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4&r2=1.4.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Clusterfs.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/FailoverDomain.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/FenceHandler.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4.2.3&r2=1.4.2.3.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Fs.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Ip.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ModelBuilder.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.8.2.6&r2=1.8.2.6.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/NFSClient.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/NFSExport.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Netfs.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Samba.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Script.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/StorageReport.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.20.2.2&r2=1.20.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/TagObject.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1.2.1&r2=1.1.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/clui_constants.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/cluster_adapters.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.120.2.20&r2=1.120.2.20.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/conga_constants.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.19.2.7&r2=1.19.2.7.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/homebase_adapters.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.34.2.11&r2=1.34.2.11.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_bridge.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.30.2.17&r2=1.30.2.17.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_communicator.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.9.2.9&r2=1.9.2.9.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/CommandError.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=NONE
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/CommandHandler.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=NONE
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/MessageLibrary.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=NONE
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ValidationError.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=NONE
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/Xenvm.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=NONE
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/var/Data.fs.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.15.2.9&r2=1.15.2.9.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/CGA_0160_Add_User.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.7.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/CGA_0170_Online_Documentation_Portlet.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/CGA_0200_Create_cluster.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.4.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/cleaner.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/congaDemoTests.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.11.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/conga_Helpers.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.14.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/conga_suite.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.11.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/loggerObject.py.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/test/tests_README.txt.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/utils/luci_admin.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.50.2.2&r2=1.50.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/utils/luci_cleanup.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4&r2=1.4.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/utils/luci_manage.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1.2.1&r2=1.1.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/make/version.in.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.21.2.13&r2=1.21.2.13.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/ricci.spec.in.in.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.12&r2=1.12.4.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/Network.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/ClientSocket.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4&r2=1.4.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.6.2.1&r2=1.6.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/Socket.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/Time.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4&r2=1.4.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/XML.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.6.2.1&r2=1.6.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/executils.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.7&r2=1.7.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/docs/cluster_api.html.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4&r2=1.4.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/docs/modules.html.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4&r2=1.4.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/docs/rpm_api.html.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/docs/service_api.html.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.1&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/docs/storage_api.html.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.5.2.1&r2=1.5.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/include/Network.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/include/Socket.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/ClusterModule.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.5.2.1&r2=1.5.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/ClusterStatus.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.15.2.1&r2=1.15.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/Clusvcadm.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.7.2.1&r2=1.7.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/Clusvcadm.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/clumon/src/daemon/Monitor.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.10.2.3&r2=1.10.2.3.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/clumon/src/daemon/Monitor.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.5&r2=1.5.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/log/LogParser.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.6.2.2&r2=1.6.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/rpm/PackageHandler.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.9.2.2&r2=1.9.2.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/service/ServiceManager.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.5.2.1&r2=1.5.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/service/ServiceManager.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/service/ServiceModule.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/ClusterNotRunningError.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/LVMClusterLockingError.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/ClusterNotQuorateError.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/ClvmdError.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.2&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/GFS1.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/GFS2.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.3&r2=1.3.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/LV.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.6.2.1&r2=1.6.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/LVM.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.7.2.4&r2=1.7.2.4.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/PV.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.4.2.1&r2=1.4.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/VG.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.8.2.1&r2=1.8.2.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/defines.h.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.6&r2=1.6.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/parted_wrapper.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.8&r2=1.8.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/ricci/Ricci.cpp.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=1.18.2.4&r2=1.18.2.4.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/test_suite/README.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/test_suite/SSLClient_send_to_ricci.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/test_suite/cacert.config.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/test_suite/generate_certs.sh.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.2.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/test_suite/send_to_ricci.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/test_suite/storage/report.xml.diff?cvsroot=cluster&only_with_tag=RHEL50_Z_branchpoint&r1=NONE&r2=1.1.6.1

/cvs/cluster/conga/Plone-2.5.2-1_CMFPlone.patch,v  -->  standard output
revision 1.1.6.1
--- conga/Plone-2.5.2-1_CMFPlone.patch
+++ -	2007-04-12 18:19:37.011016000 +0100
@@ -0,0 +1,111 @@
+diff -ur Plone-2.5.2-1.orig/CMFPlone/exportimport/configure.zcml Plone-2.5.2-1/CMFPlone/exportimport/configure.zcml
+--- Plone-2.5.2-1.orig/CMFPlone/exportimport/configure.zcml	2007-01-16 22:26:49.000000000 -0500
++++ Plone-2.5.2-1/CMFPlone/exportimport/configure.zcml	2007-01-23 16:24:53.000000000 -0500
+@@ -32,12 +32,6 @@
+      />
+ 
+   <adapter
+-     factory="Products.CMFCore.exportimport.content.StructureFolderWalkingAdapter"
+-     provides="Products.GenericSetup.interfaces.IFilesystemImporter"
+-     for="Products.ATContentTypes.interface.IATContentType"
+-     />
+-
+-  <adapter
+      factory=".propertiestool.SimpleItemWithPropertiesXMLAdapter"
+      provides="Products.GenericSetup.interfaces.IBody"
+      for="Products.CMFCore.interfaces.IMemberDataTool
+@@ -51,19 +45,6 @@
+           Products.GenericSetup.interfaces.ISetupEnviron"
+      />
+ 
+-  <!-- Mark ATCT objects as IDAVAware so CMFSetup can export/import them -->
+-  <five:implements
+-     class="Products.ATContentTypes.content.document.ATDocument"
+-     interface="Products.GenericSetup.interfaces.IDAVAware"
+-     />
+-
+-  <!-- XXX: Temporarily disable ATTopic exporting until we have an
+-       actual exporter or Marshaller -->
+-  <five:implements
+-     class="Products.ATContentTypes.content.topic.ATTopic"
+-     interface="Products.CMFPlone.exportimport.content.IDisabledExport"
+-     />
+-
+   <adapter
+      factory=".content.NullExporterAdapter"
+      provides="Products.GenericSetup.interfaces.IFilesystemExporter"
+diff -ur Plone-2.5.2-1.orig/CMFPlone/MembershipTool.py Plone-2.5.2-1/CMFPlone/MembershipTool.py
+--- Plone-2.5.2-1.orig/CMFPlone/MembershipTool.py	2007-01-16 22:26:49.000000000 -0500
++++ Plone-2.5.2-1/CMFPlone/MembershipTool.py	2007-01-23 16:20:22.000000000 -0500
+@@ -1,4 +1,3 @@
+-import PIL
+ from cStringIO import StringIO
+ from DateTime import DateTime
+ from Products.CMFCore.utils import getToolByName, _checkPermission
+@@ -587,6 +586,7 @@
+             if portrait_data == '':
+                 continue
+             try:
++                import PIL
+                 img = PIL.Image.open(StringIO(portrait_data))
+             except ConflictError:
+                 pass
+diff -ur Plone-2.5.2-1.orig/CMFPlone/setup/dependencies.py Plone-2.5.2-1/CMFPlone/setup/dependencies.py
+--- Plone-2.5.2-1.orig/CMFPlone/setup/dependencies.py	2007-01-16 22:26:49.000000000 -0500
++++ Plone-2.5.2-1/CMFPlone/setup/dependencies.py	2007-01-23 16:20:55.000000000 -0500
+@@ -107,7 +107,8 @@
+ except ImportError:
+     log(("PIL not found. Plone needs PIL 1.1.5 or newer. "
+          "Please download it from http://www.pythonware.com/products/pil/ or "
+-         "http://effbot.org/downloads/#Imaging"))
++         "http://effbot.org/downloads/#Imaging"),
++        severity=logging.INFO, optional=1)
+ 
+ try:
+     from elementtree import ElementTree
+diff -ur Plone-2.5.2-1.orig/CMFPlone/utils.py Plone-2.5.2-1/CMFPlone/utils.py
+--- Plone-2.5.2-1.orig/CMFPlone/utils.py	2007-01-16 22:26:50.000000000 -0500
++++ Plone-2.5.2-1/CMFPlone/utils.py	2007-01-23 16:24:11.000000000 -0500
+@@ -3,8 +3,6 @@
+ from os.path import join, abspath, split
+ from cStringIO import StringIO
+ 
+-from PIL import Image
+-
+ import zope.interface
+ from zope.interface import implementedBy
+ from zope.component import getMultiAdapter
+@@ -41,15 +39,6 @@
+ DANGEROUS_CHARS_REGEX = re.compile(r"[?&/:\\#]+")
+ EXTRA_DASHES_REGEX = re.compile(r"(^\-+)|(\-+$)")
+ 
+-# Settings for member image resize quality
+-PIL_SCALING_ALGO = Image.ANTIALIAS
+-PIL_QUALITY = 88
+-MEMBER_IMAGE_SCALE = (75, 100)
+-IMAGE_SCALE_PARAMS = {'scale': MEMBER_IMAGE_SCALE,
+-                      'quality': PIL_QUALITY,
+-                      'algorithm': PIL_SCALING_ALGO,
+-                      'default_format': 'PNG'}
+-
+ _marker = []
+ 
+ class BrowserView(BaseView):
+@@ -632,6 +621,17 @@
+     return security
+ 
+ def scale_image(image_file, max_size=None, default_format=None):
++    from PIL import Image
++
++    # Settings for member image resize quality
++    PIL_SCALING_ALGO = Image.ANTIALIAS
++    PIL_QUALITY = 88
++    MEMBER_IMAGE_SCALE = (75, 100)
++    IMAGE_SCALE_PARAMS = {'scale': MEMBER_IMAGE_SCALE,
++                          'quality': PIL_QUALITY,
++                          'algorithm': PIL_SCALING_ALGO,
++                          'default_format': 'PNG'}
++
+     """Scales an image down to at most max_size preserving aspect ratio
+     from an input file
+ 
--- conga/.cvsignore	2006/09/25 17:35:13	1.3
+++ conga/.cvsignore	2007/04/12 17:19:18	1.3.4.1
@@ -1,12 +1,10 @@
-Plone-2.5.tar.gz
-Zope-2.9.3.tgz
-Zope-2.9.4-final.tgz
+Plone-*.tar.gz
+Zope-*.tgz
 conga.spec.in
 conga.spec
 clustermon.spec.in
 clustermon.spec
-clustermon-*.src.rpm
+clustermon-*.rpm
 clustermon-*.tar.gz
-conga-*.src.rpm
+conga-*.rpm
 conga-*.tar.gz
-
--- conga/Makefile	2006/11/17 20:41:17	1.11.2.1
+++ conga/Makefile	2007/04/12 17:19:18	1.11.2.1.2.1
@@ -15,6 +15,8 @@
 CONGA_DIRNAME=conga-${VERSION}
 CLUSTERMON_DIRNAME=clustermon-${VERSION}
 
+DISTRO ?= el5
+
 .PHONY: conga ricci luci clustermon
 
 
--- conga/clustermon.spec.in.in	2007/01/23 22:34:28	1.18.2.12
+++ conga/clustermon.spec.in.in	2007/04/12 17:19:18	1.18.2.12.2.1
@@ -1,7 +1,7 @@
 ###############################################################################
 ###############################################################################
 ##
-##  Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+##  Copyright (C) 2006-2007 Red Hat, Inc.  All rights reserved.
 ##
 ##  This copyrighted material is made available to anyone wishing to use,
 ##  modify, copy, or redistribute it subject to the terms and conditions
@@ -194,6 +194,9 @@
 
 %changelog
 
+* Thu Apr 12 2007 Ryan McCabe <rmccabe at redhat.com> 0.9.2-6
+- Fixed bz236023 (Create/delete cluster - then access disk on node = Generic error on host: cluster tools: cman_tool errored)
+
 * Tue Jan 23 2007 Stanko Kupcevic <kupcevic at redhat.com> 0.8-30
 - Version bump
 
--- conga/conga.spec.in.in	2007/01/23 22:34:28	1.45.2.24
+++ conga/conga.spec.in.in	2007/04/12 17:19:18	1.45.2.24.2.1
@@ -1,7 +1,7 @@
 ###############################################################################
 ###############################################################################
 ##
-##  Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+##  Copyright (C) 2006-2007 Red Hat, Inc.  All rights reserved.
 ##
 ##  This copyrighted material is made available to anyone wishing to use,
 ##  modify, copy, or redistribute it subject to the terms and conditions
@@ -31,14 +31,12 @@
 %if "%{include_zope_and_plone}" == "yes"
 Source1: @@ZOPE_ARCHIVE_TAR@@
 Source2: @@PLONE_ARCHIVE_TAR@@
-Patch2:  Plone-2.5_CMFPlone.patch
+Patch2: Plone-2.5.2-1_CMFPlone.patch
 %endif
 Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 
-%if "%{include_zope_and_plone}" == "yes"
 BuildRequires: python-devel >= 2.4.1
-%endif
 BuildRequires: glibc-devel gcc-c++ libxml2-devel sed
 #BuildRequires: pam-devel
 BuildRequires: cyrus-sasl-devel >= 2.1
@@ -93,13 +91,13 @@
 Summary: Remote Management System - Management Station
 
 Requires: chkconfig initscripts
-%if "%{include_zope_and_plone}" == "yes"
 Requires: python >= 2.4.1
+%if "%{include_zope_and_plone}" == "yes"
 Provides: config(luci) = %{version}-%{release}
+# don't provide zope internals
 AutoProv: no
 %else
 AutoProv: yes
-Requires: python
 Requires: zope
 Requires: plone >= 2.5
 %endif
@@ -284,6 +282,20 @@
 
 %changelog
 
+* Thu Apr 12 2007 Ryan McCabe <rmccabe at redhat.com> 0.9.2-6
+- Fixed bz233326 (CVE-2007-0240 Conga includes version of Zope that is vulnerable to a XSS attack)
+- Fixed bz228637 (CVE-2007-1462 security alert - passwords sent back from server as input value)
+- Fixed bz236020 (Conga allows creation/rename of clusters with name greater than 15 characters)
+- Fixed bz236021 (Cluster cannot be deleted (from 'Manage Systems') - but no error results)
+- Fixed bz236025 (Entering bad password when creating a new cluster = UnboundLocalError: local variable 'e' referenced before assignment)
+- Fixed bz236026 (luci failover domain forms are missing/empty)
+- Fixed bz236027 (fence_xvm is incorrectly listed as "xmv" in virtual cluster)
+- Fixed bz236048 (Advanced options parameters settings don't do anything)
+- Fixed bz236050 (Unable to configure a virtual service)
+- Fixed bz236052 (kmod-gfs-xen not installed with Conga install)
+- Fixed bz236054 (enable shared storage' option cleared whenever there is a configuration error)
+- Fixed bz236055 (Must manually edit cluster.conf on the dom0 cluster to add "<fence_xvmd/>")
+
 * Tue Jan 23 2007 Stanko Kupcevic <kupcevic at redhat.com> 0.8-30
 - Fixed bz212445 (release blocker: prevent management page access)
 - Resolves: bz212445
--- conga/download_files	2006/09/25 17:35:13	1.3
+++ conga/download_files	2007/04/12 17:19:18	1.3.4.1
@@ -1,23 +1,16 @@
 # define archive info for Zope and Plone
 # URLs is a space delimited list of urls to download from
 
+ZOPE_ARCHIVE=Zope-2.9.7-final
+ZOPE_ARCHIVE_TAR=Zope-2.9.7-final.tgz
+ZOPE_MD5SUM=f7d900c00fe95d4cce0bc5854bc842b4
+ZOPE_URLs="http://www.zope.org/Products/Zope/2.9.7/Zope-2.9.7-final.tgz"
 
-#ZOPE_ARCHIVE=Zope-2.9.3
-#ZOPE_ARCHIVE_TAR=Zope-2.9.3.tgz
-#ZOPE_MD5SUM=4e8b4e076cadd6eb62dd4513748cb9f9
-#ZOPE_URLs="http://www.zope.org/Products/Zope/2.9.3/Zope-2.9.3.tgz"
 
-
-ZOPE_ARCHIVE=Zope-2.9.4-final
-ZOPE_ARCHIVE_TAR=Zope-2.9.4-final.tgz
-ZOPE_MD5SUM=7d7ffe62eabc84d0c438e450e344c29f
-ZOPE_URLs="http://www.zope.org/Products/Zope/2.9.4/Zope-2.9.4-final.tgz"
-
-
-
-PLONE_ARCHIVE=Plone-2.5
-PLONE_ARCHIVE_TAR=Plone-2.5.tar.gz
-PLONE_MD5SUM=0a385a1a4afbf940bb4e094ce5dcb583
-PLONE_URLs="http://superb-west.dl.sourceforge.net/sourceforge/plone/Plone-2.5.tar.gz \
-	    http://superb-east.dl.sourceforge.net/sourceforge/plone/Plone-2.5.tar.gz \
-	    http://easynews.dl.sourceforge.net/sourceforge/plone/Plone-2.5.tar.gz"
+PLONE_ARCHIVE=Plone-2.5.2-1
+PLONE_ARCHIVE_TAR=Plone-2.5.2-1.tar.gz
+PLONE_MD5SUM=b4891a3f11a0eacb13b234d530ba9af1
+PLONE_URLs="http://plone.googlecode.com/files/Plone-2.5.2-1.tar.gz \
+	http://superb-west.dl.sourceforge.net/sourceforge/plone/Plone-2.5.2-1.tar.gz \
+	    http://superb-east.dl.sourceforge.net/sourceforge/plone/Plone-2.5.2-1.tar.gz \
+	    http://easynews.dl.sourceforge.net/sourceforge/plone/Plone-2.5.2-1.tar.gz"
--- conga/luci/TODO	2006/07/18 20:33:04	1.3
+++ conga/luci/TODO	2007/04/12 17:19:18	1.3.6.1
@@ -7,7 +7,6 @@
 
 
 Cluster:
- - deploy-node/cluster
 
 
 
--- conga/luci/load_site.py	2006/11/16 19:34:52	1.14.2.1
+++ conga/luci/load_site.py	2007/04/12 17:19:18	1.14.2.1.2.1
@@ -3,6 +3,7 @@
 ##############################################################################
 #
 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (C) 2006-2007 Red Hat, Inc.
 #
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
--- conga/luci/pack.py	2006/11/16 19:34:52	1.4.2.1
+++ conga/luci/pack.py	2007/04/12 17:19:18	1.4.2.1.2.1
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+# Copyright (C) 2006-2007 Red Hat, Inc.
+
 import os, sys, string
 
 sys.path.extend((
/cvs/cluster/conga/luci/cluster/validate_config_gulm.js,v  -->  standard output
revision 1.3.6.1
--- conga/luci/cluster/validate_config_gulm.js
+++ -	2007-04-12 18:19:38.394965000 +0100
@@ -0,0 +1,23 @@
+function validate_form(form) {
+	var errors = new Array();
+	var lscount = 0;
+
+	var elem = form.getElementsByTagName('input');
+	for (var i = 0 ; i < elem.length ; i++) {
+		if (elem[i].type == 'text' && !str_is_blank(elem[i].value))
+			lscount++;
+		else if (elem[i].type == 'checkbox' && elem[i].checked)
+			lscount++;
+	}
+
+	if (lscount != 1 && lscount != 3 && lscount != 5)
+		errors.push('You must have exactly 1, 3, or 5 GULM lockservers.');
+
+	if (error_dialog(errors))
+		return (-1);
+
+	if (confirm('Update GULM properties?'))
+		form.submit();
+
+	return (0);
+}
/cvs/cluster/conga/luci/cluster/validate_create_gulm.js,v  -->  standard output
revision 1.1.6.1
--- conga/luci/cluster/validate_create_gulm.js
+++ -	2007-04-12 18:19:38.478363000 +0100
@@ -0,0 +1,65 @@
+var lockservers = new Array();
+
+function toggle_gulm(form, lock_type) {
+	var gulm_div = document.getElementById('gulm_lockservers');
+	if (!gulm_div)
+		return (-1);
+
+	var ielem = gulm_div.getElementsByTagName('input');
+
+	if (lock_type != 'gulm') {
+		lockservers = new Array();
+		for (var i = 0 ; i < ielem.length ; i++) {
+			lockservers[i] = ielem[i].value;
+			ielem[i].value = null;
+			ielem[i].disabled = 'disabled';
+		}
+		gulm_div.className = 'invisible';
+	} else {
+		for (var i = 0 ; i < ielem.length ; i++)
+			ielem[i].disabled = null;
+		for (var i = 0 ; i < lockservers.length ; i++)
+			ielem[i].value = lockservers[i];
+		gulm_div.className = null;
+		lockservers = new Array();
+	}
+}
+
+function check_gulm_lkserv() {
+	var errors = new Array();
+	var gulm_div = document.getElementById('gulm_lockservers');
+	if (!gulm_div) {
+		errors.push('Exactly 1, 3, or 5 GULM lock servers must be given.');
+		return (errors);
+	}
+
+	var lcount = 0;
+	var ielem = gulm_div.getElementsByTagName('input');
+	if (!ielem) {
+		errors.push('Exactly 1, 3, or 5 GULM lock servers must be given.');
+		return (errors);
+	}
+
+	for (var i = 0 ; i < ielem.length ; i++) {
+		if (ielem[i]) {
+			if (str_is_blank(ielem[i].value))
+				ielem[i].value = null;
+			else
+				lcount++;
+		}
+	}
+
+	if (lcount != 1 && lcount != 3 && lcount != 5)
+		errors.push('Exactly 1, 3, or 5 GULM lock servers must be given.');
+
+	return (errors);
+}
+
+function validate_cluster_create(form) {
+	if (form.lockmanager && !form.lockmanager[0].checked) {
+		var errors = check_gulm_lkserv();
+		if (error_dialog(errors))
+			return (-1);
+	}
+	return validateForm(form);
+}
/cvs/cluster/conga/luci/cluster/validate_fdom.js,v  -->  standard output
revision 1.3.6.1
--- conga/luci/cluster/validate_fdom.js
+++ -	2007-04-12 18:19:38.568934000 +0100
@@ -0,0 +1,40 @@
+function fdom_set_prioritized(form, state) {
+	var prilist = form.getElementsByTagName('input');
+	if (!prilist)
+		return (-1);
+	for (var i = 0 ; i < prilist.length ; i++) {
+		if (prilist[i].type == 'text' && prilist[i].className == 'fdom_priority')
+			prilist[i].disabled = !state || !form[prilist[i].id][0].checked;
+	}
+}
+
+function fdom_set_member(form, name, state) {
+	var prioritized = document.getElementById('prioritized');
+	if (!prioritized)
+		return (-1);
+	prioritized = prioritized.checked;
+	var member_pri_elem = document.getElementById(name);
+	if (!member_pri_elem)
+		return (-1);
+	member_pri_elem.disabled = !prioritized || !state;
+}
+
+function validate_add_fdom(form) {
+	var errors = new Array();
+
+	if (!form.name || str_is_blank(form.name.value)) {
+		set_form_err(form.name);
+		errors.append('No name was given for this failover domain.');
+	} else
+		clr_form_err(form.name);
+
+	if (error_dialog(errors))
+		return (-1);
+
+	var confirm_msg = 'Add this failover domain?';
+	if (form.oldname)
+		confirm_msg = 'Update this failover domain?';
+
+	if (confirm(confirm_msg))
+		form.submit();
+}
--- conga/luci/cluster/fence_device.js	2007/01/17 03:53:35	1.2.2.4
+++ conga/luci/cluster/fence_device.js	2007/04/12 17:19:18	1.2.2.4.2.1
@@ -190,48 +190,3 @@
 	container.appendChild(div_elem);
 	num_fences_level[fence_level - 1]++;
 }
-
-function validate_fence(master_form, container_id) {
-	var errors = new Array();
-	var div_elem = document.getElementById(container_id);
-	if (!div_elem)
-		return (-1);
-	var form_xml = '';
-
-	var form = div_elem.getElementsByTagName('form');
-	for (var i = 0 ; i < form.length ; i++) {
-		var input_elem = form[i].getElementsByTagName('input');
-		var temp = '';
-		for (var j = 0 ; j < input_elem.length ; j++) {
-			var res_type = input_elem[j].type;
-			if (res_type == 'hidden' || res_type == 'text' ||
-				res_type == 'password')
-			{
-				temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '" value="' + input_elem[j].value + '" />';
-			} else if (res_type == 'checkbox' || res_type == 'radio') {
-				if (input_elem[j].checked) {
-					temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '"';
-					if (res_type == 'checkbox')
-						temp += ' value="1"';
-					else if (res_type == 'radio')
-						temp += ' value="' + input_elem[j].value + '"';
-					temp += ' />';
-				} else if (res_type == 'checkbox') {
-					temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '" value="0" />';
-				}
-			}
-		}
-
-		var select_elem = form[i].getElementsByTagName('select');
-		for (var j = 0 ; j < select_elem.length ; j++) {
-			temp += '<input type="text" name="' + select_elem[j].name + '" value="' + select_elem[j].options[select_elem[j].options.selectedIndex].value + '" />';
-		}
-
-		form_xml += '<form id="' + form[i].getAttribute('name') + '">' + temp + '</form>';
-	}
-
-	master_form.fence_xml.value = '<formlist>' + form_xml + '</formlist>';
-
-	if (confirm('Update this node\'s fence configuration?'))
-		master_form.submit();
-}
--- conga/luci/cluster/form-chooser	2006/12/22 17:50:16	1.12.2.3
+++ conga/luci/cluster/form-chooser	2007/04/12 17:19:18	1.12.2.3.2.1
@@ -57,10 +57,10 @@
      <div metal:use-macro="here/form-macros/macros/nodelogs-form"/>
     </span>
     <span tal:omit-tag="" tal:condition="python: ptype == '18'">
-     <div metal:use-macro="here/form-macros/macros/xenvmadd-form"/>
+     <div metal:use-macro="here/form-macros/macros/vmadd-form"/>
     </span>
     <span tal:omit-tag="" tal:condition="python: ptype == '19'">
-     <div metal:use-macro="here/form-macros/macros/xenvmconfig-form"/>
+     <div metal:use-macro="here/form-macros/macros/vmconfig-form"/>
     </span>
     <span tal:omit-tag="" tal:condition="python: ptype == '20'">
      <div metal:use-macro="here/form-macros/macros/servicelist-form"/>
@@ -92,8 +92,8 @@
     <span tal:omit-tag="" tal:condition="python: ptype == '28'">
      <div metal:use-macro="here/form-macros/macros/servicerestart"/>
     </span>
-    <span tal:omit-tag="" tal:condition="python: ptype == '29'">
-     <div metal:use-macro="here/form-macros/macros/xenvmprocess"/>
+    <span tal:omit-tag="" tal:condition="python: ptype == '59'">
+     <div metal:use-macro="here/form-macros/macros/servicemigrate"/>
     </span>
     <span tal:omit-tag="" tal:condition="python: ptype == '30'">
      <div metal:use-macro="here/resource-form-macros/macros/resources-form"/>
--- conga/luci/cluster/form-macros	2007/01/16 17:38:06	1.90.2.18
+++ conga/luci/cluster/form-macros	2007/04/12 17:19:18	1.90.2.18.2.1
@@ -92,7 +92,7 @@
 		</td>
 
 		<td class="cluster cluster_action">
-			<form method="post" onSubmit="return dropdown(this.gourl)">
+			<form method="post">
 				<select name="gourl" id="cluster_action" class="cluster">
 					<option class="cluster running"
 						tal:condition="python: 'running' in cstatus and cstatus['running'] != 'true'"
@@ -116,7 +116,8 @@
 						Delete this cluster
 					</option>
 				</select>
-				<input class="cluster" type="submit" value="Go" />
+				<input class="cluster" type="button" value="Go"
+					onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 			</form>
 		</td>
 	</tr>
@@ -201,6 +202,8 @@
 	<script type="text/javascript"
 		src="/luci/homebase/validate_cluster_add.js">
 	</script>
+	<script type="text/javascript"
+		src="validate_create_gulm.js">
 	<script type="text/javascript">
 		set_page_title('Luci — cluster — Deploy a cluster');
 	</script>
@@ -208,7 +211,7 @@
 	<tal:block tal:omit-tag=""
 		tal:define="global sessionObj python: request.SESSION.get('checkRet')" />
 
-	<h1>Add a cluster</h1>
+	<h1>Create a new cluster</h1>
 
 	<form name="create_cluster" action="" method="post"
 		tal:define="
@@ -222,14 +225,14 @@
 
 		<table id="systemsTable" class="systemsTable" cellspacing="0">
 			<thead class="systemsTable">
-                <tr class="systemsTable"><td class="systemsTable" colspan="2">
-                    <div class="systemsTableTop">
-                        <strong>Cluster Name</strong>
-                        <input class="hbInputSys" type="text"
+				<tr class="systemsTable"><td class="systemsTable" colspan="2">
+					<div class="systemsTableTop">
+						<strong>Cluster Name</strong>
+						<input class="hbInputSys" type="text"
 							id="clusterName" name="clusterName"
 							tal:attributes="value add_cluster/name | nothing" />
-                    </div>
-                </td></tr>
+					</div>
+				</td></tr>
 				<tr class="systemsTable">
 					<th class="systemsTable">Node Hostname</th>
 					<th class="systemsTable">Root Password</th>
@@ -242,31 +245,40 @@
 			</thead>
 
 			<tfoot class="systemsTable">
+				<tr class="systemsTable"><td class="systemsTable" colspan="2">
+					<div class="systemsTableEnd">
+						<input type="button" value="Add a cluster node"
+							onClick="addSystem(this.form)" />
+					</div>
+				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<input type="hidden" name="trust_shown" value="1"
 						tal:condition="add_cluster" />
 					<ul class="vanilla deploy">
 						<li class="vanilla">
-							<input type="radio" name="download_pkgs"
-								value="1" checked="checked" />
+							<input type="radio" name="download_pkgs" value="1"
+								tal:attributes="checked python: (not add_cluster or not 'download_pkgs' in add_cluster or add_cluster['download_pkgs'] != 0) and 'checked' or ''" />
 							Download packages
 						</li>
 						<li class="vanilla">
-							<input type="radio" name="download_pkgs"
-								value="0" />
+							<input type="radio" name="download_pkgs" value="0"
+								tal:attributes="checked python: (add_cluster and 'download_pkgs' in add_cluster and add_cluster['download_pkgs'] == 0) and 'checked' or ''" />
 							Use locally installed packages.
 						</li>
 					</ul>
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<input type="checkbox" name="enable_storage" />
+					<input type="checkbox" name="enable_storage"
+						tal:attributes="
+							checked add_cluster/shared_storage | nothing" />
 					Enable Shared Storage Support
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<ul class="vanilla">
 						<li class="vanilla">
 							<input name="check_certs" type="checkbox"
-								tal:attributes="checked python: (add_cluster and add_cluster['check_certs']) and 'checked'" />
+								id="view_certs"
+								onchange="view_certs_only(this.form, this.checked)" />
 							View system certificates before sending any passwords.
 						</li>
 						<li class="vanilla">
@@ -280,12 +292,94 @@
 						</li>
 					</ul>
 				</td></tr>
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<div class="systemsTableEnd">
-						<input type="button" value="Add another entry"
-							onClick="addSystem(this.form)" />
-					</div>
-				</td></tr>
+
+				<tr class="systemsTable"
+					tal:condition="exists:add_cluster/gulm_support">
+					<td class="systemsTable" colspan="2">
+						<strong class="cluster">Lock Manager</strong>
+						<ul class="vanilla">
+							<li class="vanilla">
+								<input type="radio"
+									name="lockmanager" value="dlm"
+									onchange="toggle_gulm(this.form, this.value)"
+									tal:attributes="checked python: not add_cluster or not 'lockmanager' in add_cluster or add_cluster['lockmanager'] != 'gulm'"
+								>DLM (preferred)
+							</li>
+							<li class="vanilla">
+								<input type="radio"
+									name="lockmanager" value="gulm"
+									onchange="toggle_gulm(this.form, this.value)"
+									tal:attributes="checked python: add_cluster and 'lockmanager' in add_cluster and add_cluster['lockmanager'] == 'gulm'"
+								>GULM
+							</li>
+							<div id="gulm_lockservers"
+								tal:attributes="class python: (add_cluster and 'lockmanager' in add_cluster and add_cluster['lockmanager'] != 'gulm') and 'invisible' or ''">
+								<fieldset>
+								<legend class="rescfg">GULM lock server properties</legend>
+								<p>You must enter exactly 1, 3, or 5 GULM lock servers.</p>
+
+								<table class="systemsTable">
+									<tr>
+										<td class="pad_right">Lock Server 1</td>
+										<td>
+											<input type="text"
+												name="__GULM__:server1"
+												class="hostname"
+												tal:attributes="
+													disabled python: not add_cluster or not 'lockmanager' in add_cluster or add_cluster['lockmanager'] != 'gulm';
+													value add_cluster/gulm_lockservers/server1 | nothing" />
+										</td>
+									</tr>
+									<tr>
+										<td class="pad_right">Lock Server 2</td>
+										<td>
+											<input type="text"
+												name="__GULM__:server2"
+												class="hostname"
+												tal:attributes="
+													disabled python: not add_cluster or not 'lockmanager' in add_cluster or add_cluster['lockmanager'] != 'gulm';
+													value add_cluster/gulm_lockservers/server2 | nothing" />
+										</td>
+									</tr>
+									<tr>
+										<td class="pad_right">Lock Server 3</td>
+										<td>
+											<input type="text"
+												name="__GULM__:server3"
+												class="hostname"
+												tal:attributes="
+													disabled python: not add_cluster or not 'lockmanager' in add_cluster or add_cluster['lockmanager'] != 'gulm';
+													value add_cluster/gulm_lockservers/server3 | nothing" />
+										</td>
+									</tr>
+									<tr>
+										<td class="pad_right">Lock Server 4</td>
+										<td>
+											<input type="text"
+												name="__GULM__:server4"
+												class="hostname"
+												tal:attributes="
+													disabled python: not add_cluster or not 'lockmanager' in add_cluster or add_cluster['lockmanager'] != 'gulm';
+													value add_cluster/gulm_lockservers/server4 | nothing" />
+										</td>
+									</tr>
+									<tr>
+										<td class="pad_right">Lock Server 5</td>
+										<td>
+											<input type="text"
+												name="__GULM__:server5"
+												class="hostname"
+												tal:attributes="
+													disabled python: not add_cluster or not 'lockmanager' in add_cluster or add_cluster['lockmanager'] != 'gulm';
+													value add_cluster/gulm_lockservers/server5 | nothing" />
+										</td>
+									</tr>
+								</table>
+								</fieldset>
+							</div>
+						<ul>
+					</td>
+				</tr>
 			</tfoot>
 
 			<tal:block tal:define="global cur_sysnum python:0" />
@@ -313,7 +407,7 @@
 								onChange="pwd0Change(this.form)"
 								autocomplete="off"
 								tal:attributes="
-									value sys/passwd | nothing;
+									value nothing;
 									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
 									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
 									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
@@ -423,7 +517,7 @@
 
 		<div class="hbSubmit" id="hbSubmit">
 			<input type="button" name="Submit" value="Submit"
-				onClick="validateForm(this.form)" />
+				onClick="validate_cluster_create(this.form)" />
 		</div>
 	</form>
 
@@ -458,18 +552,28 @@
 				class python: 'configTab' + (configTabNum == 1 and ' configTabActive' or '');
 			">General</a>
 		</li>
-		<li class="configTab">
+		<li class="configTab"
+			tal:condition="not:clusterinfo/gulm">
 			<a tal:attributes="
 				href clusterinfo/fencedaemon_url | nothing;
 				class python: 'configTab' + (configTabNum == 2 and ' configTabActive' or '');
 			">Fence</a>
 		</li>
-		<li class="configTab">
+		<li class="configTab"
+			tal:condition="not:clusterinfo/gulm">
 			<a tal:attributes="
 				href clusterinfo/multicast_url | nothing;
 				class python: 'configTab' + (configTabNum == 3 and ' configTabActive' or '');
 			">Multicast</a>
 		</li>
+
+		<li class="configTab"
+			tal:condition="clusterinfo/gulm">
+			<a tal:attributes="
+				href clusterinfo/gulm_url | nothing;
+				class python: 'configTab' + (configTabNum == 5 and ' configTabActive' or '')">GULM</a>
+		</li>
+
 		<li class="configTab">
 			<a tal:attributes="
 				href clusterinfo/quorumd_url | nothing;
@@ -538,18 +642,62 @@
 					<table class="systemsTable">
 						<tr class="systemsTable">
 							<td class="systemsTable">
+								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#token', 55, 65);">Token Timeout</a> (ms)
+							</td>
+							<td class="systemsTable">
+								<input type="text" size="10" name="token"
+									tal:attributes="value clusterinfo/totem/token | string:10000" />
+							</td>
+						</tr>
+
+						<tr class="systemsTable">
+							<td class="systemsTable">
+								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#retransmits_before_loss', 55, 65);">Number of token retransmits before loss</a>
+							</td>
+							<td class="systemsTable">
+								<input type="text" size="10"
+									name="token_retransmits_before_loss_const"
+									tal:attributes="value clusterinfo/totem/token_retransmits_before_loss_const | string:20" />
+							</td>
+						</tr>
+
+						<tr class="systemsTable">
+							<td class="systemsTable">
+								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#join', 55, 65);">Join Timeout</a> (ms)
+							</td>
+							<td class="systemsTable">
+								<input type="text" size="10" name="join"
+									tal:attributes="value clusterinfo/totem/join | string:60" />
+							</td>
+						</tr>
+
+						<tr class="systemsTable">
+							<td class="systemsTable">
+								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#consensus', 55, 65);">Consensus Timeout</a> (ms)
+							</td>
+							<td class="systemsTable">
+								<input type="text" size="10"
+									name="consensus"
+									tal:attributes="value clusterinfo/totem/consensus | string:4800" />
+							</td>
+						</tr>
+
+<tal:comment tal:replace="nothing">
+						<tr class="systemsTable">
+							<td class="systemsTable">
 								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#secauth', 55, 65);">Secure Authentication</a>
 							</td>
 							<td class="systemsTable">
 								<input type="checkbox" name="secauth" checked="checked" />
 						</tr>
 
+
 						<tr class="systemsTable">
 							<td class="systemsTable">
 								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#rrp_mode', 55, 65);">Redundant Ring Protocol Mode</a>
 							</td>
 							<td class="systemsTable">
-								<select name="text" name="rrp_mode">
+								<select type="text" name="rrp_mode">
 									<option value="none">
 										None
 									</option>
@@ -601,16 +749,6 @@
 
 						<tr class="systemsTable">
 							<td class="systemsTable">
-								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#token', 55, 65);">Token Timeout</a> (ms)
-							</td>
-							<td class="systemsTable">
-								<input type="text" size="10" name="token"
-									tal:attributes="value string:5000" />
-							</td>
-						</tr>
-
-						<tr class="systemsTable">
-							<td class="systemsTable">
 								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#token_retransmit', 55, 65);">Token Retransmit</a> (ms)
 							</td>
 							<td class="systemsTable">
@@ -632,33 +770,11 @@
 
 						<tr class="systemsTable">
 							<td class="systemsTable">
-								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#retransmits_before_loss', 55, 65);">Number of retransmits before loss</a>
-							</td>
-							<td class="systemsTable">
-								<input type="text" size="10"
-									name="retransmits_before_loss"
-									tal:attributes="value string:4" />
-							</td>
-						</tr>
-
-						<tr class="systemsTable">
-							<td class="systemsTable">
-								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#join', 55, 65);">Join Timeout</a> (ms)
-							</td>
-							<td class="systemsTable">
-								<input type="text" size="10" name="join"
-									tal:attributes="value string:100" />
-							</td>
-						</tr>
-
-						<tr class="systemsTable">
-							<td class="systemsTable">
-								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#consensus', 55, 65);">Consensus Timeout</a> (ms)
+								<a class="cluster_help" href="javascript:popup_window('/luci/doc/config_rhel5#send_join', 55, 65);">Maximum time to wait before sending a join message</a> (ms)
 							</td>
 							<td class="systemsTable">
-								<input type="text" size="10"
-									name="consensus"
-									tal:attributes="value string:100" />
+								<input type="text" size="10" name="send_join"
+									tal:attributes="value string:0" />
 							</td>
 						</tr>
 
@@ -782,6 +898,7 @@
 									tal:attributes="value string:47" />
 							</td>
 						</tr>
+</tal:comment>
 					</table>
 				</td></tr>
 			</table>
@@ -842,6 +959,14 @@
 							tal:attributes="value clusterinfo/pjd" />
 					</td>
 				</tr>
+				<tr class="systemsTable">
+					<td class="systemsTable">
+						<span class="cluster_help" title="Enable if you will be running a VM cluster on this physical cluster">Run XVM fence daemon</td>
+					<td class="systemsTable">
+						<input type="checkbox" name="run_xvmd"
+							tal:attributes="checked python: ('fence_xvmd' in clusterinfo and clusterinfo['fence_xvmd']) and 'checked' or ''" />
+					</td>
+				</tr>
 			</tbody>
 
 			<tfoot class="systemsTable">
@@ -891,7 +1016,12 @@
 							onClick="disable_mcast('mcast_address');"
 							tal:attributes="checked python: clusterinfo['is_mcast'] != 'True'"
 						/>
-						Let cluster choose the multicast address
+						<tal:block tal:condition="python:os_version == 'rhel4'">
+							Do not use multicast
+						</tal:block>
+						<tal:block tal:condition="python:os_version != 'rhel4'">
+							Let cluster choose the multicast address
+						</tal:block>
 					</td>
 				</tr>
 
@@ -902,12 +1032,20 @@
 							tal:attributes="checked python: clusterinfo['is_mcast'] == 'True'"
 
 						/>
-						Specify the multicast address manually
+						<tal:block tal:condition="python:os_version == 'rhel4'">
+							Use multicast
+						</tal:block>
+						<tal:block tal:condition="python:os_version != 'rhel4'">
+							Specify the multicast address manually
+						</tal:block>
 					</td>
 				</tr>
 
 				<tr class="systemsTable">
-					<td class="systemsTable" colspan="2">
+					<td class="systemsTable">
+						Multicast address
+					</td>
+					<td class="systemsTable">
 						<input type="text" name="mcast_address" id="mcast_address"
 							tal:attributes="
 								disabled python: clusterinfo['is_mcast'] != 'True' and '1' or '0';
@@ -959,14 +1097,14 @@
 				<tr class="systemsTable" id="st_row"><td class="systemsTable" id="st_col">
 					<input type="radio" name="quorumd" value="False"
 						onClick="disableChildrenInput('quorumdisk');"
-						tal:attributes="checked python: clusterinfo['is_quorumd'] != 'True'"/>
+						tal:attributes="checked python: (not clusterinfo['is_quorumd']) and 'checked' or ''" />
 					Do not use a Quorum Partition
 				</td></tr>
 
 				<tr class="systemsTable"><td class="systemsTable">
 					<input type="radio" name="quorumd" value="True"
 						onClick="enableChildrenInput('quorumdisk');"
-						tal:attributes="checked python: clusterinfo['is_quorumd'] == 'True'"/>
+						tal:attributes="checked python: (clusterinfo['is_quorumd']) and 'checked' or ''" />
 						Use a Quorum Partition
 				</td></tr>
 			</tbody>
@@ -981,48 +1119,48 @@
 			<tr class="systemsTable">
 				<td class="systemsTable">Interval</td>
 				<td class="systemsTable">
-					<input type="text" name="interval" value=""
-						tal:attributes="value clusterinfo/interval"/>
+					<input type="text" name="interval"
+						tal:attributes="value clusterinfo/interval | nothing" />
 				</td>
 			</tr>
 
 			<tr class="systemsTable">
 				<td class="systemsTable">Votes</td>
 				<td class="systemsTable">
-					<input type="text" name="votes" value=""
-						tal:attributes="value clusterinfo/votes"/>
+					<input type="text" name="votes"
+						tal:attributes="value clusterinfo/votes | nothing" />
 				</td>
 			</tr>
 
 			<tr class="systemsTable">
 				<td class="systemsTable">TKO</td>
 				<td class="systemsTable">
-					<input type="text" name="tko" value=""
-						tal:attributes="value clusterinfo/tko"/>
+					<input type="text" name="tko"
+						tal:attributes="value clusterinfo/tko | nothing" />
 				</td>
 			</tr>
 
 			<tr class="systemsTable">
 				<td class="systemsTable">Minimum Score</td>
 				<td class="systemsTable">
-					<input type="text" name="min_score" value=""
-						tal:attributes="value clusterinfo/min_score"/>
+					<input type="text" name="min_score"
+						tal:attributes="value clusterinfo/min_score | nothing" />
 				</td>
 			</tr>
 
 			<tr class="systemsTable">
 				<td class="systemsTable">Device</td>
 				<td class="systemsTable">
-					<input type="text" name="device" value=""
-						tal:attributes="value clusterinfo/device"/>
+					<input type="text" name="device"
+						tal:attributes="value clusterinfo/device | nothing" />
 				</td>
 			</tr>
 
 			<tr class="systemsTable">
 				<td class="systemsTable">Label</td>
 				<td class="systemsTable">
-					<input type="text" name="label" value=""
-						tal:attributes="value clusterinfo/label"/>
+					<input type="text" name="label"
+						tal:attributes="value clusterinfo/label | nothing" />
 				</td>
 			</tr>
 		</table>
@@ -1039,9 +1177,6 @@
 
 				<tr class="systemsTable">
 					<th class="systemsTable">
-						<div class="systemsTableTop">Name</div>
-					</th>
-					<th class="systemsTable">
 						<div class="systemsTableTop">Path to Program</div>
 					</th>
 					<th class="systemsTable">
@@ -1057,22 +1192,22 @@
 			</thead>
 
 			<tbody class="systemsTable" id="heuristicList"
-				tal:define="global heuristics clusterinfo/hlist">
+				tal:define="global heuristics clusterinfo/hlist | nothing">
 
-				<tal:block tal:condition="python: not len(heuristics)">
+				<tal:block tal:condition="python: not heuristics or not len(heuristics)">
 				<input type="hidden" name="num_heuristics" id="num_heuristics" value="0">
 				<tr class="systemsTable" id="heuristic0">
 					<td class="systemsTable">
-						<input class="qdname qdisk" type="text" name="heuristic0:hname" id="heuristic0:hname" value="">
-					</td>
-					<td class="systemsTable">
-						<input class="qdpath qdisk" type="text" name="heuristic0:hprog" id="heuristic0:hprog" value="">
+						<input class="qdpath qdisk" type="text"
+							name="heuristic0:hprog" id="heuristic0:hprog" />
 					</td>
 					<td class="systemsTable">
-						<input class="qdint qdisk" type="text" name="heuristic0:hinterval" id="heuristic0:hinterval" value="">
+						<input class="qdint qdisk" type="text"
+							name="heuristic0:hinterval" id="heuristic0:hinterval" />
 					</td>
 					<td class="systemsTable">
-						<input class="qdscore qdisk" type="text" name="heuristic0:hscore" id="heuristic0:hscore" value="">
+						<input class="qdscore qdisk" type="text"
+							name="heuristic0:hscore" id="heuristic0:hscore" />
 					</td>
 					<td class="systemsTable">
 						<img class="qdisk deleteRow"
@@ -1097,32 +1232,27 @@
 					tal:define="global curHeur python: curHeur + 1">
 
 					<td class="systemsTable">
-						<input class="qdname qdisk" type="text"
-							tal:attributes="
-								value heuristic/hname;
-								id python: 'heuristic' + str(curHeur) + ':hname';
-								name python: 'heuristic' + str(curHeur) + ':hname';"/>
-					</td>
-					<td class="systemsTable">
 						<input class="qdpath qdisk" type="text"
 							tal:attributes="
 								value heuristic/hprog;
 								id python: 'heuristic' + str(curHeur) + ':hprog';
-								name python: 'heuristic' + str(curHeur) + ':hprog';"/>
+								name python: 'heuristic' + str(curHeur) + ':hprog'" />
 					</td>
+
 					<td class="systemsTable">
 						<input class="qdint qdisk" type="text"
 							tal:attributes="
 								value heuristic/hinterval;
 								id python: 'heuristic' + str(curHeur) + ':hinterval';
-								name python: 'heuristic' + str(curHeur) + ':hinterval';"/>
+								name python: 'heuristic' + str(curHeur) + ':hinterval'" />
 					</td>
+
 					<td class="systemsTable">
 						<input class="qdscore qdisk" type="text"
 							tal:attributes="
 								value heuristic/hscore;
 								id python: 'heuristic' + str(curHeur) + ':hscore';
-								name python: 'heuristic' + str(curHeur) + ':hscore';"/>
+								name python: 'heuristic' + str(curHeur) + ':hscore'" />
 					</td>
 					<td class="systemsTable">
 						<img class="qdisk deleteRow"
@@ -1131,14 +1261,15 @@
 							onClick="delete_qdisk_heur(this, document.quorum_partition);"
 							tal:attributes="
 								id python: 'heuristic' + str(curHeur) + ':hdel';
-								name python: 'heuristic' + str(curHeur) + ':hdel';"/>
+								name python: 'heuristic' + str(curHeur) + ':hdel'" />
 					</td>
 				</tr>
 				</tal:block>
 			</tbody>
+
 			<tfoot>
 				<tr class="systemsTable"><td class="systemsTable">
-					<input class="addrow" type="button" value="Add another heuristic" onClick="addHeuristic('heuristicList');">
+					<input class="addrow" type="button" value="Add another heuristic" onClick="addHeuristic('heuristicList')">
 				</td></tr>
 			</tfoot>
 		</table>
@@ -1148,14 +1279,106 @@
 		<div class="spacing configTabContent"></div>
 		<div class="hbSubmit spacing configTabContent">
 			<input type="button" value="Apply"
-				onClick="validate_form(this.form);"
-			>
+				onClick="validate_form(this.form)" />
 		</div>
-		<script tal:condition="python: clusterinfo['is_quorumd'] != 'True'">
+		<script tal:condition="not: clusterinfo/is_quorumd">
 			disableChildrenInput('quorumdisk');
 		</script>
 		</form>
 	</div>
+
+	<tal:block tal:condition="clusterinfo/gulm">
+	<div id="configTabContent" tal:condition="python: configTabNum == 5">
+		<script type="text/javascript"
+			src="/luci/homebase/homebase_common.js">
+		</script>
+		<script type="text/javascript"
+			src="/luci/cluster/validate_config_gulm.js">
+		</script>
+
+		<form name="basecluster" action="" method="post">
+			<input type="hidden" name="cluster_version"
+				tal:attributes="value os_version | nothing" />
+			<input type="hidden" name="pagetype"
+				tal:attributes="value request/pagetype | request/form/pagetype"
+			/>
+			<input type="hidden" name="configtype" value="gulm" />
+			<input type="hidden" name="clustername"
+				tal:attributes="value request/clustername | clusterinfo/clustername | nothing" />
+
+		<strong class="cluster">GULM Configuration</strong><br/>
+		<table id="systemsTable" class="systemsTable" cellspacing="0">
+			<thead>
+				<tr class="systemsTable" align="left">
+					<th class="systemsTable">Node</th>
+					<th class="systemsTable">Lock Server</th>
+				</tr>
+			</thead>
+			<tbody>
+				<tal:block tal:repeat="c clusterinfo/gulm_lockservers">
+					<tr class="systemsTable">
+						<td class="systemsTable">
+							<span tal:replace="python:c[0]" />
+						</td>
+						<td class="systemsTable">
+							<input type="checkbox"
+								tal:attributes="
+									name python:c[0];
+									checked python:c[1]" />
+						</td>
+					</tr>
+				</tal:block>
+
+				<tr><td colspan="2">
+					<div class="spacing" />
+					<p><em class="cluster">You may have exactly 1, 3, or 5 GULM lock servers, in any combination of the hosts checked above and given below.</em></p>
+				</td></tr>
+
+				<tr>
+					<td class="pad_right">External Lock Server 1</td>
+					<td>
+						<input type="text" name="__GULM__:server1"
+							class="hostname" value="" />
+					</td>
+				</tr>
+				<tr>
+					<td class="pad_right">External Lock Server 2</td>
+					<td>
+						<input type="text" name="__GULM__:server2"
+							class="hostname" value="" />
+					</td>
+				</tr>
+				<tr>
+					<td class="pad_right">External Lock Server 3</td>
+					<td>
+						<input type="text" name="__GULM__:server3"
+							class="hostname" value="" />
+					</td>
+				</tr>
+				<tr>
+					<td class="pad_right">External Lock Server 4</td>
+					<td>
+						<input type="text" name="__GULM__:server4"
+							class="hostname" value="" />
+					</td>
+				</tr>
+				<tr>
+					<td class="pad_right">External Lock Server 5</td>
+					<td>
+						<input type="text" name="__GULM__:server5"
+							class="hostname" value="" />
+					</td>
+				</tr>
+			</tbody>
+		</table>
+
+		<div class="spacing configTabContent"></div>
+		<div class="hbSubmit spacing configTabContent">
+			<input type="button" value="Apply"
+				onClick="validate_form(this.form)"/>
+		</div>
+	</div>
+	</tal:block>
 </tal:block>
 </div>
 
@@ -1177,7 +1400,6 @@
 	<option name="fence_egenera" value="fence_egenera">Egenera SAN Controller</option>
 	<option name="fence_bladecenter" value="fence_bladecenter">IBM Blade Center</option>
 	<option name="fence_bullpap" value="fence_bullpap">Bull PAP</option>
-	<option name="fence_rps10" value="fence_rps10">RPS10 Serial Switch</option>
 	<option name="fence_xvm" value="fence_xvm">Virtual Machine Fencing</option>
 	<option name="fence_scsi" value="fence_scsi">SCSI Fencing</option>
 </div>
@@ -1223,7 +1445,7 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 	</div>
@@ -1271,14 +1493,25 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1329,14 +1562,25 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1378,14 +1622,25 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1428,14 +1683,25 @@
 				<td>Password</td>
 				<td>
 					<input name="passwd" type="password" autocomplete="off"
-						tal:attributes="value cur_fencedev/passwd | nothing" />
+						tal:attributes="value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1477,13 +1743,25 @@
 				<td>Password</td>
 				<td>
 					<input name="passwd" type="password" autocomplete="off"
-						tal:attributes="value cur_fencedev/passwd | nothing" />
+						tal:attributes="value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
 				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
+				</td>
+			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1525,14 +1803,25 @@
 				<td>Password</td>
 				<td>
 					<input name="passwd" type="password" autocomplete="off"
-						tal:attributes="value cur_fencedev/passwd | nothing" />
+						tal:attributes="value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1582,14 +1871,25 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1640,14 +1940,25 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1689,17 +2000,28 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
 				</td>
 			</tr>
-		</table>
-
-		<tal:block tal:condition="exists: cur_fencedev">
-			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
-				tal:attributes="value cur_fencedev/name | nothing" />
-		</tal:block>
-
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
+				</td>
+			</tr>
+		</table>
+
+		<tal:block tal:condition="exists: cur_fencedev">
+			<input type="hidden" name="existing_device" value="1" />
+			<input type="hidden" name="orig_name"
+				tal:attributes="value cur_fencedev/name | nothing" />
+		</tal:block>
+
 		<input type="hidden" name="sharable" value="1" />
 		<input type="hidden" name="fence_type" value="fence_vixel" />
 	</div>
@@ -1736,7 +2058,7 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1785,7 +2107,7 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1828,14 +2150,25 @@
 				<td>Password</td>
 				<td>
 					<input name="passwd" type="password" autocomplete="off"
-						tal:attributes="value cur_fencedev/passwd | nothing" />
+						tal:attributes="value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1886,14 +2219,25 @@
 					<input name="passwd" type="password" autocomplete="off"
 						tal:attributes="
 							disabled cur_fencedev/isShared | nothing;
-							value cur_fencedev/passwd | nothing" />
+							value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 		</table>
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1936,7 +2280,7 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -1966,12 +2310,12 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
 		<input type="hidden" name="sharable" value="1" />
-		<input type="hidden" name="fence_type" value="xvm" />
+		<input type="hidden" name="fence_type" value="fence_xvm" />
 	</div>
 </div>
 
@@ -1997,12 +2341,12 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
 		<input type="hidden" name="sharable" value="1" />
-		<input type="hidden" name="fence_type" value="scsi" />
+		<input type="hidden" name="fence_type" value="fence_scsi" />
 	</div>
 </div>
 
@@ -2040,7 +2384,18 @@
 				<td>Password</td>
 				<td>
 					<input name="passwd" type="password" autocomplete="off"
-						tal:attributes="value cur_fencedev/passwd | nothing" />
+						tal:attributes="value nothing" />
+				</td>
+			</tr>
+			<tr>
+				<td>
+					<span title="Full path to a script to generate fence password">Password Script (optional)</span>
+				</td>
+				<td>
+					<input type="text" name="passwd_script"
+						tal:attributes="
+							disabled cur_fencedev/isShared | nothing;
+							value cur_fencedev/passwd_script | nothing" />
 				</td>
 			</tr>
 			<tr>
@@ -2067,7 +2422,7 @@
 
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 
@@ -2094,7 +2449,7 @@
 		</table>
 		<tal:block tal:condition="exists: cur_fencedev">
 			<input type="hidden" name="existing_device" value="1" />
-			<input type="hidden" name="old_name"
+			<input type="hidden" name="orig_name"
 				tal:attributes="value cur_fencedev/name | nothing" />
 		</tal:block>
 		<input type="hidden" name="fence_type" value="fence_manual" />
@@ -2213,6 +2568,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_apc" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2255,6 +2611,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_egenera" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2290,6 +2647,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_wti" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2325,6 +2683,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_brocade" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2360,6 +2719,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_vixel" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2395,6 +2755,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_sanbox2" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2430,6 +2791,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_mcdata" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2465,6 +2827,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_gnbd" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2500,6 +2863,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_bladecenter" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2535,6 +2899,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_bullpap" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2557,6 +2922,7 @@
 			</tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_scsi" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2592,6 +2958,7 @@
 			</td></tr>
 		</table>
 
+		<input type="hidden" name="fence_type" value="fence_xvm" />
 		<input type="hidden" name="fence_instance" value="1" />
 		<input tal:condition="exists: cur_instance"
 			type="hidden" name="existing_instance" value="1" />
@@ -2630,62 +2997,62 @@
 <div metal:define-macro="fencedev-instance-cond-ladder"
 	tal:condition="exists: cur_fence_type">
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_apc'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_apc'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-apc" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_mcdata'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_mcdata'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-mcdata" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_wti'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_wti'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-wti" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_brocade'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_brocade'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-brocade" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_sanbox2'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_sanbox2'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-sanbox2" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_vixel'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_vixel'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-vixel" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_gnbd'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_gnbd'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-gnbd" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_egenera'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_egenera'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-egenera" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_bullpap'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_bullpap'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-bullpap" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_scsi'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_scsi'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-scsi" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_xvm'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_xvm'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-xvm" />
 	</tal:block>
 
-    <tal:block tal:condition="python: cur_fence_type == 'fence_bladecenter'">
+	<tal:block tal:condition="python: cur_fence_type == 'fence_bladecenter'">
 		<tal:block
 			metal:use-macro="here/form-macros/macros/fence-instance-form-bladecenter" />
 	</tal:block>
@@ -2698,6 +3065,9 @@
 	<script type="text/javascript"
 		src="/luci/cluster/fence_device.js">
 	</script>
+	<script type="text/javascript"
+		src="/luci/cluster/validate_fence.js">
+	</script>
 	<script type="text/javascript">
 		set_page_title('Luci — cluster — nodes — properties');
 	</script>
@@ -2730,7 +3100,7 @@
 
 			<td class="cluster node node_action"
 				tal:condition="python: nodeinfo['nodestate'] == '0' or nodeinfo['nodestate'] == '1'">
-				<form method="post" onSubmit="return dropdown(this.gourl)">
+				<form method="post">
 				<select name="gourl">
 					<option value="">Choose a Task...</option>
 					<option tal:attributes="value nodeinfo/jl_url"
@@ -2748,18 +3118,20 @@
 						tal:condition="python: not 'ricci_error' in nodeinfo">
 						Delete this node</option>
 				</select>
-				<input type="submit" value="Go"/>
+				<input type="button" value="Go"
+					onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 				</form>
 			</td>
 
 			<td class="cluster node node_action"
 				tal:condition="python: nodeinfo['nodestate'] != '0' and nodeinfo['nodestate'] != '1'">
-				<form method="post" onSubmit="return dropdown(this.gourl)">
+				<form method="post">
 				<select name="gourl">
 					<option value="">Choose a Task...</option>
 					<option tal:attributes="value nodeinfo/fence_url | nothing">Fence this node</option>
 				</select>
-				<input type="submit" value="Go"/>
+				<input type="button" value="Go"
+					onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 				</form>
 			</td>
 		</tr>
@@ -2770,7 +3142,13 @@
 				<span tal:attributes="class python: 'cluster node ' + status_class"
 					tal:content="python: cluster_node_status_str" />
 			</td>
+		</tr>
 
+		<tr class="cluster node info_middle"
+			tal:condition="nodeinfo/gulm_lockserver">
+			<td class="cluster node node_status" colspan="2">
+				This node is a GULM lock server.
+			</td>
 		</tr>
 
 		<tr class="cluster node info_bottom"
@@ -2803,7 +3181,8 @@
 		<tfoot class="systemsTable">
 			<tr class="systemsTable"><td class="systemsTable" colspan="3">
 				<div class="systemsTableEnd">
-					<input type="Submit" value="Update node daemon properties" />
+					<input type="button" value="Update node daemon properties"
+						onclick="if (confirm('Update daemon properties?')) this.form.submit()" />
 				</div>
 			</td></tr>
 		</tfoot>
@@ -3074,7 +3453,7 @@
 						<input type="hidden" name="pagetype" value="58" />
 						<input type="button"
 							value="Update main fence properties"
-							onclick="validate_fence(this.form, 'fence_list_level1')" />
+							onclick="validate_node_fence_form(this.form, 'fence_list_level1')" />
 					</form>
 				</div>
 			</td>
@@ -3090,7 +3469,7 @@
 						<input type="hidden" name="pagetype" value="58" />
 						<input type="button"
 							value="Update backup fence properties"
-							onclick="validate_fence(this.form, 'fence_list_level2')" />
+							onclick="validate_node_fence_form(this.form, 'fence_list_level2')" />
 					</form>
 				</div>
 			</td>
@@ -3138,7 +3517,7 @@
 				</td>
 
 				<td class="node node_action" tal:condition="python: nd['status'] == '0' or nd['status'] == '1'">
-					<form method="post" onSubmit="return dropdown(this.gourl)">
+					<form method="post">
 						<select class="node" name="gourl">
 							<option value="">Choose a Task...</option>
 							<option tal:attributes="value nd/jl_url">
@@ -3149,18 +3528,20 @@
 							<option tal:attributes="value nd/fence_it_url">Fence this node</option>
 							<option tal:attributes="value nd/reboot_url">Reboot this node</option>
 							<option value="">----------</option>
-							<option tal:attributes="value nd/delete_url">Delete</option>
+							<option tal:attributes="value nd/delete_url">Delete this node</option>
 						</select>
-						<input type="submit" value="Go"/>
+						<input type="button" value="Go"
+							onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 					</form>
 				</td>
 				<td class="node node_action" tal:condition="python: nd['status'] != '0' and nd['status'] != '1'">
-					<form method="post" onSubmit="return dropdown(this.gourl)">
+					<form method="post">
 						<select class="node" name="gourl">
 							<option value="">Choose a Task...</option>
 							<option tal:attributes="value nd/fence_it_url | nothing">Fence this node</option>
 						</select>
-						<input type="submit" value="Go"/>
+						<input type="button" value="Go"
+							onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 					</form>
 				</td>
 			</tr>
@@ -3172,6 +3553,13 @@
 				</td>
 			</tr>
 
+			<tr class="node info_middle"
+				tal:condition="nd/gulm_lockserver">
+				<td class="node node_status" colspan="2">
+					This node is a GULM lock server.
+				</td>
+			</tr>
+
 			<tr class="node info_bottom">
 				<td class="node node_services">
 					<strong class="cluster node">Services on this Node:</strong>
@@ -3262,6 +3650,9 @@
 			global cur_cluster_name add_cluster/name | request/clustername | request/form/clusterName | nothing">
 
 		<h2>Add a node to <span tal:replace="cur_cluster_name | string:this cluster" /></h2>
+
+		<input type="hidden" name="addnode" value="1" />
+
 		<input type="hidden" name="clusterName"
 			tal:attributes="value cur_cluster_name | string:[unknown]" />
 
@@ -3285,21 +3676,24 @@
 			</thead>
 
 			<tfoot class="systemsTable">
+				<tr class="systemsTable"><td class="systemsTable" colspan="2">
+					<div class="systemsTableEnd">
+						<input type="button" value="Add another node"
+							onClick="addSystem(this.form)" />
+					</div>
+				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<input type="hidden" name="trust_shown" value="1"
 						tal:condition="add_cluster" />
 					<ul class="vanilla deploy">
 						<li class="vanilla">
 							<input type="radio" name="download_pkgs" value="1"
-								tal:attributes="
-									checked add_system/download_pkgs | string:checked" />
+								tal:attributes="checked python: (not add_cluster or not 'download_pkgs' in add_cluster or add_cluster['download_pkgs'] != 0) and 'checked' or ''" />
 							Download packages
 						</li>
 						<li class="vanilla">
 							<input type="radio" name="download_pkgs" value="0"
-								tal:attributes="
-									checked not: add_system/download_pkgs | nothing" />
-							
+								tal:attributes="checked python: (add_cluster and 'download_pkgs' in add_cluster and add_cluster['download_pkgs'] == 0) and 'checked' or ''" />
 							Use locally installed packages.
 						</li>
 					</ul>
@@ -3307,14 +3701,15 @@
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<input type="checkbox" name="enable_storage"
 						tal:attributes="
-							checked add_system/shared_storage | nothing" />
+							checked add_cluster/shared_storage | nothing" />
 					Enable Shared Storage Support
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<ul class="vanilla">
 						<li class="vanilla">
 							<input name="check_certs" type="checkbox"
-								tal:attributes="checked python: (add_cluster and add_cluster['check_certs']) and 'checked'" />
+								id="view_certs"
+								onchange="view_certs_only(this.form, this.checked)" />
 							View system certificates before sending any passwords.
 						</li>
 						<li class="vanilla"
@@ -3328,12 +3723,6 @@
 						</li>
 					</ul>
 				</td></tr>
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<div class="systemsTableEnd">
-						<input type="button" value="Add another entry"
-							onClick="addSystem(this.form)" />
-					</div>
-				</td></tr>
 			</tfoot>
 
 			<tal:block tal:define="global cur_sysnum python:0" />
@@ -3361,7 +3750,7 @@
 								autocomplete="off"
 								onChange="pwd0Change(this.form)"
 								tal:attributes="
-									value sys/passwd | nothing;
+									value nothing;
 									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
 									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
 									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
@@ -3487,15 +3876,18 @@
 
 			<tr class="cluster service info_top">
 				<td class="cluster service service_name">
-					<strong class="cluster service">Service Name:</strong>
+					<strong class="cluster service">Service Name</strong>
 					<a tal:attributes="
 						href svc/cfgurl;
 						class python: 'cluster service ' + (running and 'running' or 'stopped')"
 						tal:content="svc/name" />
+					<tal:block tal:condition="exists:svc/is_vm">
+						(virtual service)
+					</tal:block>
 				</td>
 
 				<td class="cluster service service_action">
-					<form method="post" onSubmit="return dropdown(this.gourl)">
+					<form method="post">
 						<select name="gourl">
 							<option value="">Choose a Task...</option>
 							<option
@@ -3506,7 +3898,7 @@
 
 							<option
 								tal:condition="running"
-								tal:attributes="value svc/retstarturl| nothing"
+								tal:attributes="value svc/restarturl | nothing"
 								tal:content="string:Restart this service" />
 
 							<option
@@ -3517,14 +3909,43 @@
 							<option
 								tal:condition="not: running"
 								tal:attributes="value svc/enableurl | nothing"
-								tal:content="string:Start this service" />
+								tal:content="string:Enable this service" />
 
 							<option
 								tal:condition="not: running"
 								tal:attributes="value svc/delurl | nothing"
 								tal:content="string:Delete this service" />
+
+							<option value="">----------</option>
+
+							<tal:block tal:condition="not: running">
+								<tal:block tal:repeat="starturl svc/links">
+									<option
+										tal:condition="not:exists: starturl/migrate"
+										tal:attributes="value starturl/url">Start this service on <span tal:replace="starturl/nodename" /></option>
+								</tal:block>
+							</tal:block>
+
+							<tal:block tal:condition="running">
+								<tal:block tal:repeat="starturl svc/links">
+									<option
+										tal:condition="not:exists: starturl/migrate"
+										tal:attributes="value starturl/url">Relocate this service to <span tal:replace="starturl/nodename" /></option>
+								</tal:block>
+
+								<tal:block tal:condition="svc/is_vm | nothing">
+									<option value="">----------</option>
+									<tal:block tal:repeat="starturl svc/links">
+										<option
+											tal:condition="exists: starturl/migrate"
+											tal:attributes="value starturl/url">Migrate this service to <span tal:replace="starturl/nodename" /></option>
+									</tal:block>
+								</tal:block>
+							</tal:block>
+
 						</select>
-						<input type="submit" value="Go"/>
+						<input type="button" value="Go"
+							onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 					</form>
 				</td>
 			</tr>
@@ -3554,30 +3975,170 @@
 	</tal:block>
 </div>
 
-<div metal:define-macro="xenvmadd-form">
-  <form method="get" action="" tal:attributes="action python:request['baseurl'] + '?clustername=' + request['clustername'] + '&pagetype=29'">
-  <h4>Path to configuration file: </h4><input type="text" name="xenvmpath" value=""/>
-  <h4>Name of configuration file: </h4><input type="text" name="xenvmname" value=""/>
-  <input type="submit" value="Create Xen VM"/>
-  </form>
-</div>
-
-<div metal:define-macro="xenvmconfig-form">
-  <h4>Properties for Xen VM <font color="green"><span tal:content="request/servicename"/></font></h4>
-  <span tal:define="global xeninfo python:here.getXenVMInfo(modelb, request)">
-  <form method="get" action="" tal:attributes="action python:request['baseurl'] + '?clustername=' + request['clustername'] + '&pagetype=29&servicename=' + request['servicename']">
-  <h4>Path to configuration file: </h4><input type="text" name="xenvmpath" value="" tal:attributes="value xeninfo/path"/>
-  <h4>Name of configuration file: </h4><input type="text" name="xenvmname" value="" tal:attributes="value xeninfo/name"/>
-  <input type="button" value="Delete"/>
-  <input type="submit" value="Update"/>
-  </form>
- </span>
-</div>
+<div metal:define-macro="vmadd-form">
+<form method="post" action="">
+	<input type="hidden" name="clustername"
+		tal:attributes="value request/clustername | nothing" />
+
+	<input type="hidden" name="pagetype"
+		tal:attributes="value request/pagetype | nothing" />
 
-<div metal:define-macro="xenvmprocess">
-	<span tal:define="retrn python:here.processXenVM(request)"/>
+	<div class="service_comp_list">
+	<table class="systemsTable">
+		<thead class="systemsTable">
+			<tr class="systemsTable"><td class="systemsTable">
+				<p class="reshdr">Create a Virtual Machine Service</p>
+			</td></tr>
+		<tfoot class="systemsTable">
+			<tr class="systemsTable">
+				<td>Automatically start this service</td>
+				<td>
+					<input type="checkbox" name="autostart" checked="checked">
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Run exclusive</td>
+				<td>
+					<input type="checkbox" name="exclusive">
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Failover Domain</td>
+				<td>
+					<select name="domain">
+						<option value="" selected="selected">None</option>
+						<tal:block tal:repeat="f python:here.get_fdom_names(modelb)">
+							<option tal:content="f"
+								tal:attributes="value f" />
+						</tal:block>
+					</select>
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Recovery policy</td>
+				<td>
+					<select name="recovery">
+						<option value="">Select a recovery policy</option>
+						<option name="relocate" value="relocate">Relocate</option>
+						<option name="restart" value="restart">Restart</option>
+						<option name="disable" value="disable">Disable</option>
+					</select>
+				</td>
+			</tr>
+			<tr class="systemsTable"><td colspan="2">
+				<div class="hbSubmit">
+					<input type="submit" value="Create Virtual Machine Service" />
+				</div>
+			</td></tr>
+		</tfoot>
+		<tbody class="systemsTable">
+			<tr class="systemsTable">
+				<td><span class="cluster_help" title="e.g., guest1 if the VM config file is at /etc/xen/guest1">Virtual machine name</span></td>
+				<td><input type="text" name="vmname" value="" /></td>
+			</tr>
+			<tr class="systemsTable">
+				<td><span class="cluster_help" title="e.g., /etc/xen/">Path to VM configuration files</span></td>
+				<td><input type="text" name="vmpath" value="" /></td>
+			</tr>
+		</tbody>
+	</table>
+	</div>
+</form>
 </div>
 
+<div metal:define-macro="vmconfig-form">
+<form method="post" action=""
+	tal:define="vminfo python:here.getVMInfo(modelb, request)">
+
+	<input type="hidden" name="clustername"
+		tal:attributes="value request/clustername | nothing" />
+
+	<input type="hidden" name="pagetype"
+		tal:attributes="value request/pagetype | nothing" />
+
+	<input type="hidden" name="oldname"
+		tal:attributes="value vminfo/name | nothing" />
+
+	<div class="service_comp_list">
+	<table class="systemsTable">
+		<thead class="systemsTable">
+			<tr class="systemsTable"><td class="systemsTable">
+				<p class="reshdr">Properties for <tal:block tal:replace="vminfo/name | string:virtual machine service"/></p>
+			</td></tr>
+		<tfoot class="systemsTable">
+			<tr class="systemsTable">
+				<td>Automatically start this service</td>
+				<td>
+					<input type="checkbox" name="autostart"
+						tal:attributes="checked python: ('autostart' in vminfo and vminfo['autostart'] != '0') and 'checked' or ''" />
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Run exclusive</td>
+				<td>
+					<input type="checkbox" name="exclusive"
+						tal:attributes="checked python: ('exclusive' in vminfo and vminfo['exclusive'] != '0') and 'checked' or ''" />
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Failover Domain</td>
+				<td>
+					<select name="domain">
+						<option value="" tal:content="string:None"
+							tal:attributes="selected python: (not 'domain' in vminfo or not vminfo['domain']) and 'selected' or ''" />
+						<tal:block tal:repeat="f python:here.get_fdom_names(modelb)">
+							<option tal:content="f"
+								tal:attributes="
+									value f;
+									selected python: ('domain' in vminfo and vminfo['domain'] == f) and 'selected' or ''" />
+						</tal:block>
+					</select>
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Recovery policy</td>
+				<td>
+					<select name="recovery">
+						<option value="">Select a recovery policy</option>
+						<option name="relocate" value="relocate"
+							tal:content="string:Relocate"
+							tal:attributes="selected python: ('recovery' in vminfo and vminfo['recovery'] == 'relocate') and 'selected' or ''" />
+						<option name="restart" value="restart"
+							tal:content="string:Restart"
+							tal:attributes="selected python: ('recovery' in vminfo and vminfo['recovery'] == 'restart') and 'selected' or ''" />
+						<option name="disable" value="disable"
+							tal:content="string:Disable"
+							tal:attributes="selected python: ('recovery' in vminfo and vminfo['recovery'] == 'disable') and 'selected' or ''" />
+					</select>
+				</td>
+			</tr>
+			<tr class="systemsTable"><td colspan="2">
+				<div class="hbSubmit">
+					<input name="submit" type="submit" value="Update Virtual Machine Service" />
+					<input name="delete" type="submit" value="Delete Virtual Machine Service" />
+				</div>
+			</td></tr>
+		</tfoot>
+		<tbody class="systemsTable">
+			<tr class="systemsTable">
+				<td><span class="cluster_help" title="e.g., guest1 if the VM config file is at /etc/xen/guest1">Virtual machine name</span></td>
+				<td>
+					<input type="text" name="vmname"
+						tal:attributes="value vminfo/name | nothing" />
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td><span class="cluster_help" title="e.g., /etc/xen/">Path to VM configuration files</span></td>
+				<td>
+					<input type="text" name="vmpath"
+						tal:attributes="value vminfo/path | nothing" />
+				</td>
+			</tr>
+		</tbody>
+	</table>
+	</div>
+</form>
+</div>
 
 <div metal:define-macro="serviceadd-form">
 	<script type="text/javascript">
@@ -3586,6 +4147,8 @@
 	<tal:block metal:use-macro="here/form-macros/macros/service-config-head-macro" />
 
 	<h2>Add a Service</h2>
+	<tal:block tal:define="
+		global clusterinfo python: here.getClusterInfo(modelb, request)" />
 
 	<div id="resskel" class="invisible">
 		<tal:block metal:use-macro="here/resource-form-macros/macros/service-compose-macro" />
@@ -3610,6 +4173,35 @@
 						<input type="checkbox" name="autostart" checked="checked" />
 					</td>
 				</tr>
+				<tr class="systemsTable">
+					<td class="systemsTable">Run exclusive</td>
+					<td class="systemsTable">
+						<input type="checkbox" name="exclusive">
+					</td>
+				</tr>
+				<tr class="systemsTable">
+					<td class="systemsTable">Failover Domain</td>
+					<td class="systemsTable">
+						<select name="domain">
+							<option value="" selected="selected">None</option>
+							<tal:block tal:repeat="f sinfo/fdoms">
+								<option tal:content="f"
+									tal:attributes="value f" />
+							</tal:block>
+						</select>
+					</td>
+				</tr>
+				<tr class="systemsTable">
+					<td class="systemsTable">Recovery policy</td>
+					<td class="systemsTable">
+						<select name="recovery">
+							<option value="">Select a recovery policy</option>
+							<option name="relocate" value="relocate">Relocate</option>
+							<option name="restart" value="restart">Restart</option>
+							<option name="disable" value="disable">Disable</option>
+						</select>
+					</td>
+				</tr>
 			</table>
 		</form>
 	</div>
@@ -3618,8 +4210,6 @@
 
 	<div class="service_comp_list">
 		<form name="master" method="post">
-		<tal:block
-			tal:define="global clusterinfo python: here.getClusterInfo(modelb, request)" />
 		<input type="button" value="Add a resource to this service"
 			onclick="add_child_resource(this.form);" />
 		<input type="hidden" name="pagetype"
@@ -3634,6 +4224,9 @@
 		<input type="hidden" name="tree_level" value="-1" />
 		<input type="hidden" name="svc_name" value="" />
 		<input type="hidden" name="autostart" value="-1" />
+		<input type="hidden" name="exclusive" value="-1" />
+		<input type="hidden" name="recovery" />
+		<input type="hidden" name="domain" />
 		<input type="hidden" name="form_xml" />
 		<input type="hidden" name="action" value="add" />
 		</form>
@@ -3643,9 +4236,8 @@
 		<tal:block tal:repeat="gr global_resources">
 			<tal:block tal:define="
 				global res gr;
-				global type res/type;
+				global type python: 'tag_name' in res and res['tag_name'] or '';
 				global resourceIsRef python: True" />
-
 			<tal:block metal:use-macro="here/form-macros/macros/serviceconfig-type-macro" />
 		</tal:block>
 	</div>
@@ -3661,10 +4253,19 @@
 
 	<tal:block tal:define="
 		result python: here.serviceStart(ricci_agent, request)" />
-
-	<!-- <span metal:use-macro="here/form-macros/macros/serviceconfig-form"/> -->
 </div>
 
+<div metal:define-macro="servicemigrate">
+	<script type="text/javascript">
+		set_page_title('Luci — cluster — services — Migrate a virtual service');
+	</script>
+
+	<tal:block tal:define="
+		global ricci_agent ri_agent | python: here.getRicciAgentForCluster(request)" />
+
+	<tal:block tal:define="
+		result python: here.serviceMigrate(ricci_agent, request)" />
+</div>
 
 <div metal:define-macro="servicerestart">
 	<script type="text/javascript">
@@ -3676,8 +4277,6 @@
 
 	<tal:block tal:define="
 		result python: here.serviceRestart(ricci_agent, request)" />
-
-	<!-- <span metal:use-macro="here/form-macros/macros/serviceconfig-form"/> -->
 </div>
 
 <div metal:define-macro="servicestop">
@@ -3690,50 +4289,62 @@
 
 	<span tal:define="
 		result python: here.serviceStop(ricci_agent, request)" />
-
-	<!-- <span metal:use-macro="here/form-macros/macros/serviceconfig-form"/> -->
 </div>
 
 <div metal:define-macro="serviceconfig-type-macro" tal:omit-tag="">
-	<tal:block tal:condition="python: type == 'ip' or type == 'IP Address: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/ip_macro" />
+	<tal:block tal:condition="python: type == 'ip'">
+		<div metal:use-macro="here/resource-form-macros/macros/ip_macro" />
 	</tal:block>
 
-	<tal:block tal:condition="python: type == 'fs' or type == 'File System: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/fs_macro" />
+	<tal:block tal:condition="python: type == 'fs'">
+		<div metal:use-macro="here/resource-form-macros/macros/fs_macro" />
 	</tal:block>
 
-	<tal:block tal:condition="python: type == 'gfs' or type == 'GFS: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/gfs_macro" />
+	<tal:block tal:condition="python: type == 'gfs' or type == 'clusterfs'">
+		<div metal:use-macro="here/resource-form-macros/macros/gfs_macro" />
 	</tal:block>
 
-	<tal:block tal:condition="python: type == 'nfsm' or type == 'NFS Mount: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/nfsm_macro" />
+	<tal:block tal:condition="python: type == 'netfs'">
+		<div metal:use-macro="here/resource-form-macros/macros/nfsm_macro"/>
 	</tal:block>
 
-	<tal:block tal:condition="python: type == 'nfsx' or type == 'NFS Export: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/nfsx_macro" />
+	<tal:block tal:condition="python: type == 'nfsexport'">
+		<div metal:use-macro="here/resource-form-macros/macros/nfsx_macro"/>
 	</tal:block>
 
-	<tal:block tal:condition="python: type == 'nfsc' or type == 'NFS Client: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/nfsc_macro" />
+	<tal:block tal:condition="python: type == 'nfsclient'">
+		<div metal:use-macro="here/resource-form-macros/macros/nfsc_macro"/>
 	</tal:block>
 
-	<tal:block
-		tal:condition="python: type == 'smb' or type == 'Samba Service: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/smb_macro" />
+	<tal:block tal:condition="python: type == 'smb'">
+		<div metal:use-macro="here/resource-form-macros/macros/smb_macro" />
 	</tal:block>
 
-	<tal:block tal:condition="python: type == 'script' or type == 'Script: '">
-		<tal:block
-			metal:use-macro="here/resource-form-macros/macros/scr_macro" />
+	<tal:block tal:condition="python: type == 'script'">
+		<div metal:use-macro="here/resource-form-macros/macros/scr_macro" />
+	</tal:block>
+
+	<tal:block tal:condition="python: type == 'apache'">
+		<div metal:use-macro="here/resource-form-macros/macros/apache_macro" />
+	</tal:block>
+
+	<tal:block tal:condition="python: type == 'openldap'">
+		<div metal:use-macro="here/resource-form-macros/macros/openldap_macro" />
+	</tal:block>
+
+	<tal:block tal:condition="python: type == 'mysql'">
+		<div metal:use-macro="here/resource-form-macros/macros/mysql_macro" />
+	</tal:block>
+
+	<tal:block tal:condition="python: type == 'lvm'">
+		<div metal:use-macro="here/resource-form-macros/macros/lvm_macro" />
+	</tal:block>
+
+	<tal:block tal:condition="python: type == 'postgres-8'">
+		<div metal:use-macro="here/resource-form-macros/macros/postgres-8_macro" />
+	</tal:block>
+	<tal:block tal:condition="python: type == 'tomcat-5'">
+		<div metal:use-macro="here/resource-form-macros/macros/tomcat-5_macro" />
 	</tal:block>
 </div>
 
@@ -3778,7 +4389,7 @@
 			</td>
 			<td class="cluster service service_action"
 				tal:condition="python: sinfo and 'innermap' in sinfo">
-				<form method="post" onSubmit="return dropdown(this.gourl)">
+				<form method="post">
 					<input type="hidden" name="pagetype" tal:attributes="
 						value request/pagetype | request/form/pagetype | nothing" />
 					<select name="gourl"
@@ -3787,23 +4398,53 @@
 
 						<option value="">Choose a Task...</option>
 						<tal:block tal:condition="running">
+							<option
+								tal:attributes="value innermap/restarturl">Restart this service</option>
+
+							<option
+								tal:attributes="value innermap/disableurl">Disable this service</option>
+
 							<option value="">----------</option>
-							<option value="" tal:attributes="value innermap/restarturl">Restart this service</option>
-							<option value="" tal:attributes="value innermap/disableurl">Disable this service</option>
-							<option value="">----------</option>
-							<option tal:repeat="starturl innermap/links" value="" tal:attributes="value starturl/url">Start this service on <span tal:replace="starturl/nodename"/></option>
+
+							<tal:block tal:repeat="starturl innermap/links">
+								<option
+									tal:condition="not:exists: starturl/migrate"
+									tal:attributes="value starturl/url">Relocate this service to <span tal:replace="starturl/nodename" />
+								</option>
+							</tal:block>
+
+							<tal:block tal:condition="svc/is_vm | nothing">
+								<option value="">----------</option>
+								<tal:block tal:repeat="starturl innermap/links">
+									<option
+										tal:condition="exists: starturl/migrate"
+										tal:attributes="value starturl/url">Migrate this service to <span tal:replace="starturl/nodename" /></option>
+								</tal:block>
+							</tal:block>
 						</tal:block>
 
 						<tal:block tal:condition="not: running">
+							<option
+								tal:attributes="value innermap/enableurl">Enable this service</option>
 							<option value="">----------</option>
-							<option value="" tal:attributes="value innermap/enableurl">Enable this service</option>
+
+							<tal:block tal:repeat="starturl innermap/links">
+								<option
+									tal:condition="not:exists: starturl/migrate"
+									tal:attributes="value starturl/url">Start this service on <span tal:replace="starturl/nodename" />
+								</option>
+							</tal:block>
+
 							<option value="">----------</option>
+
 							<option
 								tal:attributes="value innermap/delurl | nothing"
 								tal:content="string:Delete this service" />
 						</tal:block>
 					</select>
-					<input type="submit" value="Go"/>
+
+					<input type="button" value="Go"
+						onclick="if (this.form.gourl[this.form.gourl.selectedIndex].value && confirm(this.form.gourl[this.form.gourl.selectedIndex].text + '?')) return dropdown(this.form.gourl)" />
 				</form>
 			</td>
 		</tr>
@@ -3818,6 +4459,8 @@
 	<br/>
 
 	<h2>Service Composition</h2>
+	<tal:block tal:define="
+		global clusterinfo python: here.getClusterInfo(modelb, request)" />
 
 	<div id="resskel" class="invisible">
 		<tal:block metal:use-macro="here/resource-form-macros/macros/service-compose-macro" />
@@ -3834,7 +4477,7 @@
 			tal:replace="structure python: '<div class=nothing>'" />
 
 		<tal:block tal:define="
-			global type res/type;
+			global type python: 'tag_name' in res and res['tag_name'] or '';
 			global resourceIsRef res/ref_object | nothing" />
 
 		<tal:block metal:use-macro="here/form-macros/macros/serviceconfig-type-macro" />
@@ -3847,19 +4490,56 @@
 	<div class="service_comp_list">
 		<form name="service_name_form">
 			<table class="rescfg">
-				<tr><td>
-				Automatically start this service
-				</td>
-				<td><input type="checkbox" name="autostart"
-						tal:attributes="checked python: ('autostart' in sinfo and sinfo['autostart'].lower() != 'false') and 'checked'" /></td></tr>
+				<tr>
+					<td>Automatically start this service</td>
+					<td><input type="checkbox" name="autostart"
+							tal:attributes="checked python: ('autostart' in sinfo and sinfo['autostart'].lower() != 'false') and 'checked'" />
+					</td>
+				</tr>
+				<tr>
+					<td>Run exclusive</td>
+					<td><input type="checkbox" name="exclusive"
+							tal:attributes="checked python: ('exclusive' in sinfo and sinfo['exclusive'].lower() != 'false') and 'checked'" />
+					</td>
+				</tr>
+				<tr>
+					<td>Failover Domain</td>
+					<td>
+						<select name="domain">
+							<option value=""
+								tal:attributes="selected python: (not 'domain' in sinfo or not sinfo['domain']) and 'selected' or ''">None</option>
+							<tal:block tal:repeat="f sinfo/fdoms">
+								<option tal:content="f"
+									tal:attributes="
+										value f;
+										selected python: ('domain' in sinfo and sinfo['domain'] == f) and 'selected' or ''" />
+							</tal:block>
+						</select>
+					</td>
+				</tr>
+				<tr class="systemsTable">
+					<td>Recovery policy</td>
+					<td>
+						<select name="recovery">
+							<option value="">Select a recovery policy</option>
+							<option name="relocate" value="relocate"
+								tal:content="string:Relocate"
+								tal:attributes="selected python: ('recovery' in sinfo and sinfo['recovery'] == 'relocate') and 'selected' or ''" />
+							<option name="restart" value="restart"
+								tal:content="string:Restart"
+								tal:attributes="selected python: ('recovery' in sinfo and sinfo['recovery'] == 'restart') and 'selected' or ''" />
+							<option name="disable" value="disable"
+								tal:content="string:Disable"
+								tal:attributes="selected python: ('recovery' in sinfo and sinfo['recovery'] == 'disable') and 'selected' or ''" />
+						</select>
+					</td>
+				</tr>
 			</table>
 			<input type="hidden" name="service_name"
 				tal:attributes="value sinfo/name | string:1" />
 		</form>
 
 		<form name="master" method="post">
-		<tal:block
-			tal:define="global clusterinfo python: here.getClusterInfo(modelb, request)" />
 		<input type="hidden" name="pagetype"
 			tal:attributes="
 				value request/pagetype | request/form/pagetype | nothing" />
@@ -3874,6 +4554,9 @@
 		<input type="hidden" name="tree_level" value="-1" />
 		<input type="hidden" name="svc_name" value="" />
 		<input type="hidden" name="autostart" value="-1" />
+		<input type="hidden" name="exclusive" value="-1" />
+		<input type="hidden" name="recovery" />
+		<input type="hidden" name="domain" />
 		<input type="hidden" name="form_xml" />
 		<input type="hidden" name="action" value="edit" />
 		</form>
@@ -3883,9 +4566,8 @@
 		<tal:block tal:repeat="gr global_resources">
 			<tal:block tal:define="
 				global res gr;
-				global type res/type;
+				global type python: 'tag_name' in res and res['tag_name'] or '';
 				global resourceIsRef python: True" />
-
 			<tal:block metal:use-macro="here/form-macros/macros/serviceconfig-type-macro" />
 		</tal:block>
 	</div>
@@ -4003,23 +4685,119 @@
 	</div>
 </div>
 
+<tal:block metal:define-macro="fdom-macro">
+<script type="text/javascript"
+	src="/luci/homebase/homebase_common.js">
+</script>
+<script type="text/javascript"
+	src="/luci/cluster/validate_fdom.js">
+</script>
+
+<form method="post" action="">
+	<input type="hidden" name="clustername"
+		tal:attributes="value request/clustername | nothing" />
+	<input type="hidden" name="pagetype"
+		tal:attributes="value request/pagetype | nothing" />
+	<input type="hidden" name="oldname"
+		tal:condition="exists: fdom/name"
+		tal:attributes="value fdom/name | nothing" />
+	
+	<table class="systemsTable" width="100%">
+		<thead class="systemsTable">
+			<tr class="systemsTable">
+				<td><strong>Failover Domain Name</strong></td>
+				<td>
+					<input type="text" name="name"
+						tal:attributes="value fdom/name | nothing" />
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Prioritized</td>
+				<td>
+					<input type="checkbox" name="prioritized" id="prioritized"
+						onchange="fdom_set_prioritized(this.form, this.checked)"
+						tal:attributes="checked python: (fdom and 'prioritized' in fdom and fdom['prioritized'] == '1') and 'checked' or ''" />
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td>Restrict failover to this domain's members</td>
+				<td>
+					<input type="checkbox" name="restricted"
+						tal:attributes="checked python: (fdom and 'restricted' in fdom and fdom['restricted'] == '1') and 'checked' or ''" />
+				</td>
+			</tr>
+			<tr class="systemsTable">
+				<td class="systemsTable" colspan="2">
+					<p></p>
+					<p class="reshdr">Failover domain membership</p>
+				</td>
+			</tr>
+		</thead>
+
+		<tfoot class="systemsTable">
+			<tr class="systemsTable"><td>
+				<div class="hbSubmit">
+					<input type="button" name="add" value="Submit"
+						onclick="validate_add_fdom(this.form)" />
+				</div>
+			</td></tr>
+		</tfoot>
+
+		<tbody width="60%">
+			<tr class="systemsTable">
+				<th class="systemsTable" width="33%">Node</th>
+				<th class="systemsTable" width="10%">Member</th>
+				<th class="systemsTable" width="57%">Priority</th>
+			</tr>
+			<tal:block tal:repeat="n python:here.getnodes(modelb)">
+				<tr class="systemsTable">
+					<td class="systemsTable" width="33%">
+						<tal:block tal:replace="n" />
+					<td class="systemsTable" width="10%">
+						<input type="checkbox"
+							onchange="fdom_set_member(this.form, this.name, this.checked)"
+							tal:attributes="
+								checked python: ('members' in fdom and n in fdom['members']) and 'checked' or '';
+								name n" />
+					</td>
+					<td class="systemsTable" width="75%">
+						<input type="text" class="fdom_priority"
+							tal:attributes="
+								id n;
+								name python: '__PRIORITY__' + n;
+								value python: ('members' in fdom and n in fdom['members'] and 'priority' in fdom['members'][n]) and fdom['members'][n]['priority'] or '1';
+								disabled python: (not fdom or not 'prioritized' in fdom or fdom['prioritized'] != '1' or not 'members' in fdom or not n in fdom['members']) and 'disabled' or ''" />
+					</td>
+				</tr>
+			</tal:block>
+		</tbody>
+	</table>
+</form>
+
+</tal:block>
+
 <div metal:define-macro="fdomadd-form">
 	<script type="text/javascript">
 		set_page_title('Luci — cluster — failover domains — Add a failover domain');
 	</script>
-	<h2>Failover Domain Add Form</h2>
-  <tal:block tal:define="allnodes python:here.getFdomNodes(request)"/>
+
+	<h2>Add a Failover Domain</h2>
+	<tal:block tal:define="fdom python:{}">
+		<tal:block metal:use-macro="here/form-macros/macros/fdom-macro" />
+	</tal:block>
 </div>
 
 <div metal:define-macro="fdomconfig-form">
 	<script type="text/javascript">
 		set_page_title('Luci — cluster — failover domains — Configure a failover domain');
 	</script>
-	<h2>Failover Domain Configuration Form</h2>
 </div>
 
 <div metal:define-macro="fdom-form">
 	<h2>Failover Domain Form</h2>
+	<tal:block tal:define="fdom python:here.getFdomInfo(modelb, request)">
+		<tal:block metal:use-macro="here/form-macros/macros/fdom-macro" />
+	</tal:block>
 </div>
 
 <div metal:define-macro="fdomprocess-form">
@@ -4028,29 +4806,34 @@
 
 <div metal:define-macro="fencedevs-form">
 	<script type="text/javascript">
-		set_page_title('Luci — cluster — fence devices');
+		set_page_title('Luci — cluster — shared fence devices');
 	</script>
-	<h2>Shared Fence Devices for Cluster: <span tal:content="request/clustername"/></h2>
-  <tal:block tal:define="global fencedevinfo python: here.getFencesInfo(modelb, request)"/>
-<tal:block tal:define="global fencedevs python: fencedevinfo['fencedevs']"/>
-  <span tal:repeat="fencedev fencedevs">
-   <h3>Agent type: <span tal:content="fencedev/pretty_name"/></h3>
-   <h3>Name: <font style="color:green"><a class="running" tal:attributes="href fencedev/cfgurl"><span tal:content="fencedev/name"/></a></font></h3>
-   <h3>Nodes using this device for fencing:</h3>
-   <ul>
-     <tal:block tal:define="global usednodes python:fencedev['nodesused']"/>
-     <span tal:condition="python: len(usednodes) == 0">
-      <li>No nodes currently employ this fence device</li>
-     </span>
-    <span tal:repeat="usednode usednodes">
-     <li><font color="green">
-      <a class="runned" href="" tal:attributes="href usednode/nodeurl"><tal:block tal:content="usednode/nodename"/>
-      </a></font>
-     </li>
-    </span>
-   </ul>
-   <hr/>
-  </span>
+
+	<h2>Shared Fence Devices for Cluster: <span tal:replace="request/clustername" /></h2>
+
+	<tal:block tal:define="
+		global fencedevinfo python: here.getFencesInfo(modelb, request);
+		global fencedevs python: fencedevinfo['fencedevs']" />
+
+	<tal:block tal:repeat="fencedev fencedevs">
+		<h3>Agent type: <span tal:content="fencedev/pretty_name"/></h3>
+		<h3>Name: <a class="running" tal:attributes="href fencedev/cfgurl"><span tal:replace="fencedev/name" /></a></h3>
+		<h3>Nodes using this device for fencing:</h3>
+		<ul>
+			<tal:block tal:define="global usednodes python:fencedev['nodesused']"/>
+			<tal:block tal:condition="python: len(usednodes) == 0">
+				<li>No nodes currently employ this fence device</li>
+			</tal:block>
+
+			<tal:block tal:repeat="usednode usednodes">
+				<li><a class="cluster node"
+						tal:attributes="href usednode/nodeurl"
+						tal:content="usednode/nodename" />
+				</li>
+			</tal:block>
+		</ul>
+		<hr/>
+	</tal:block>
 </div>
 
 <div metal:define-macro="fencedevlist-form">
@@ -4064,6 +4847,15 @@
 	<script type="text/javascript"
 		src="/luci/cluster/fence_device.js">
 	</script>
+
+	<script type="text/javascript"
+		src="/luci/homebase/homebase_common.js">
+	</script>
+
+	<script type="text/javascript"
+		src="/luci/cluster/validate_fence.js">
+	</script>
+
 	<script type="text/javascript">
 		set_page_title('Luci — cluster — fence devices - Add a new fence device');
 	</script>
@@ -4101,10 +4893,13 @@
 					<div id="fence_container">
 					</div>
 					<div class="hbSubmit">
-						<input type="submit"
-							value="Add this shared fence device" />
+						<input type="button"
+							value="Add this shared fence device"
+							onclick="validate_fence_form(this.form)" />
+
 						<input type="hidden" name="pagetype"
 							value="51" id="pagetype" />
+
 						<input type="hidden" name="clustername" id="pagetype"
 							tal:attributes="value request/clustername" />
 				</form>
@@ -4130,12 +4925,12 @@
 
 	<tal:block tal:condition="exists: fencedevs/fencedevs">
 		<table class="systemsTable">
-	        <thead class="systemsTable">
+			<thead class="systemsTable">
 				<tr class="systemsTable">
 					<th class="systemsTable" width="100">Name</th>
 					<th class="systemsTable" width="100">Type</th>
 					<th class="systemsTable" width="75">Configure</th>
-            	</tr>
+				</tr>
 			</thead>
 			<tr class="systemsTable" tal:repeat="f fencedevs/fencedevs">
 				<td class="systemsTable" tal:content="f/name | string:[unknown]"/>
@@ -4226,13 +5021,25 @@
 		<tal:block metal:use-macro="here/form-macros/macros/fence-form-manual" />
 	</tal:block>
 
-    <tal:block tal:condition="exists:cur_fencedev/unknown">
+	<tal:block tal:condition="exists:cur_fencedev/unknown">
 		<tal:block metal:use-macro="here/form-macros/macros/fence-form-unknown" />
 	</tal:block>
 </div>
 
 
 <div metal:define-macro="fencedev-form">
+	<script type="text/javascript">
+		set_page_title('Luci — cluster — fence devices - Configure a fence device');
+	</script>
+
+	<script type="text/javascript"
+		src="/luci/homebase/homebase_common.js">
+	</script>
+
+	<script type="text/javascript"
+		src="/luci/cluster/validate_fence.js">
+	</script>
+
 	<h2>Fence Device Form</h2>
 
 	<div class="cluster fencedev fence">
@@ -4254,13 +5061,12 @@
 			<input type="hidden" name="pagetype" value="54" />
 			<input type="hidden" name="clustername"
 				tal:attributes="value request/clustername" />
-			<input type="hidden" name="orig_name"
-				tal:attributes="value request/fencename"/>
-  			<input type="hidden" name="fencename"
+			<input type="hidden" name="fencename"
 				tal:attributes="value request/fencename" />
 
 			<div class="hbSubmit">
-				<input type="submit" value="Update this fence device" />
+				<input type="button" value="Update this fence device"
+					onclick="validate_fence_form(this.form)" />
 			</div>
 		</form>
 
@@ -4273,7 +5079,8 @@
 			<input type="hidden" name="orig_name"
 				tal:attributes="value request/fencename" />
 			<div class="hbSubmit">
-				<input type="submit" value="Delete this fence device" />
+				<input type="button" value="Delete this fence device"
+					onclick="if (confirm('Delete this fence device?')) this.form.submit()" />
 			</div>
 		</form>
 	</div>
@@ -4286,22 +5093,28 @@
 <div metal:define-macro="conf_editor-form">
 	<h2>Edit cluster.conf</h2>
 	<form method="post"
-	      tal:attributes="action python: './?' + request['QUERY_STRING']"
-	      tal:define="ret python: here.process_cluster_conf_editor(request)">
-	  <span tal:content="structure python: ret['msg'].replace('\n', '<br/>')"/>
-	  <textarea name="new_cluster_conf"
-		    tal:attributes="rows python: len(ret['cluster_conf'].splitlines()) + 8"
-		    tal:content="structure ret/cluster_conf"></textarea>
-	  <input tal:attributes="type  string:hidden;
-	                         name  string:pagetype;
-	                         value python:request['pagetype']"/>
-	  <input tal:attributes="type  string:hidden;
-	                         name  string:clustername;
-	                         value python:request['clustername']"/>
-	  <input type="button" 
-	         value="Reset"
-	         tal:attributes="onclick python:'window.location.assign(\'./?pagetype=' + request['pagetype'] + '&clustername=' + request['clustername'] + '\')'"/>
-	  <input type="submit" value="Propagate"/>
+		tal:attributes="action python: './?' + request['QUERY_STRING']"
+		tal:define="ret python: here.process_cluster_conf_editor(request)">
+
+	<span tal:content="structure python: ret['msg'].replace('\n', '<br/>')" />
+
+	<textarea name="new_cluster_conf"
+		tal:attributes="rows python: len(ret['cluster_conf'].splitlines()) + 8"
+		tal:content="structure ret/cluster_conf">
+	</textarea>
+
+	<input type="hidden" name="pagetype"
+		 tal:attributes="
+			value request/pagetype | nothing" />
+
+	<input type="hidden" name="clustername"
+		tal:attributes="
+			value request/clustername | nothing" />
+
+	<input type="button" value="Reset"
+		tal:attributes="onclick python:'window.location.assign(\'./?pagetype=' + request['pagetype'] + '&clustername=' + request['clustername'] + '\')'" />
+
+	<input type="submit" value="Propagate" />
 	</form>
 </div>
 
--- conga/luci/cluster/index_html	2006/12/22 17:50:16	1.20.2.6
+++ conga/luci/cluster/index_html	2007/04/12 17:19:18	1.20.2.6.2.1
@@ -32,10 +32,6 @@
 			global ri_agent nothing;
 			global busywaiting python:None" />
 
-		<tal:block tal:condition="not: hascluster">
-		    <meta googaa="ooo"/>
-		</tal:block>
-
 		<tal:block tal:condition="hascluster">
 			<tal:block tal:define="
 				global ri_agent python:here.getRicciAgentForCluster(request);
--- conga/luci/cluster/resource-form-macros	2006/12/07 17:54:31	1.21.2.3
+++ conga/luci/cluster/resource-form-macros	2007/04/12 17:19:18	1.21.2.3.2.1
@@ -106,7 +106,10 @@
 		<form>
 		<select onChange="swap_div_elem(this.form.parentNode,
 									this.options[this.selectedIndex].value);">
-			<option name="blank" value="blank" checked>Select a resource type</option>
+			<option name="blank" value="blank" checked>
+				Select a resource type
+			</option>
+
 			<option name="IP" value="IP">IP address</option>
 			<option name="FS" value="FS">File system</option>
 			<option name="GFS" value="GFS">GFS file system</option>
@@ -115,6 +118,13 @@
 			<option name="NFSX" value="NFSX">NFS export</option>
 			<option name="SCR" value="SCR">Script</option>
 			<option name="SMB" value="SMB">Samba</option>
+
+			<option name="APACHE" value="APACHE">Apache</option>
+			<option name="LVM" value="LVM">LVM</option>
+			<option name="MYSQL" value="MYSQL">MySQL</option>
+			<option name="OPENLDAP" value="OPENLDAP">Open LDAP</option>
+			<option name="POSTGRES-8" value="POSTGRES-8">PostgreSQL 8</option>
+			<option name="TOMCAT-5" value="TOMCAT-5">Tomcat 5</option>
 		</select>
 		</form>
 	</p>
@@ -129,6 +139,12 @@
 		<div metal:use-macro="here/resource-form-macros/macros/nfsc_macro" />
 		<div metal:use-macro="here/resource-form-macros/macros/smb_macro" />
 		<div metal:use-macro="here/resource-form-macros/macros/scr_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/apache_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/mysql_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/lvm_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/openldap_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/postgres-8_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/tomcat-5_macro" />
 	</div>
 </div>
 
@@ -147,6 +163,13 @@
 			<option name="NFSX" value="NFSX">NFS export</option>
 			<option name="SCR" value="SCR">Script</option>
 			<option name="SMB" value="SMB">Samba</option>
+
+			<option name="APACHE" value="APACHE">Apache</option>
+			<option name="LVM" value="LVM">LVM</option>
+			<option name="MYSQL" value="MYSQL">MySQL</option>
+			<option name="OPENLDAP" value="OPENLDAP">Open LDAP</option>
+			<option name="POSTGRES-8" value="POSTGRES-8">PostgreSQL 8</option>
+			<option name="TOMCAT-5" value="TOMCAT-5">Tomcat 5</option>
 		</select>
 		</form>
 	</p>
@@ -184,6 +207,12 @@
 		<div metal:use-macro="here/resource-form-macros/macros/nfsc_macro" />
 		<div metal:use-macro="here/resource-form-macros/macros/smb_macro" />
 		<div metal:use-macro="here/resource-form-macros/macros/scr_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/apache_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/mysql_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/lvm_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/openldap_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/postgres-8_macro" />
+		<div metal:use-macro="here/resource-form-macros/macros/tomcat-5_macro" />
 	</div>
 </div>
 
@@ -199,7 +228,10 @@
 	</script>
 
 	<tal:block tal:define="
-		global res python: here.getResourceInfo(modelb, request);" />
+		global res python: here.getResourceInfo(modelb, request)" />
+
+	<tal:block tal:define="
+		global clusterinfo python: here.getClusterInfo(modelb, request)" />
 
 	<h2>Add a Resource</h2>
 
@@ -225,7 +257,7 @@
 		</thead>
 		<tr class="systemsTable">
 			<td class="systemsTable" tal:content="res/name" />
-			<td class="systemsTable" tal:content="res/tag_name" />
+			<td class="systemsTable" tal:content="res/type" />
 			<td>
 				<a class="cluster resource"
 					tal:content="string: configure"
@@ -235,7 +267,6 @@
 	</table>
 </div>
 
-
 <div metal:define-macro="resourceprocess-form">
 	<h2>Resource <span tal:replace="python: ('edit' in request and request['edit']) and 'Edited' or 'Added'" /></h2>
 
@@ -255,45 +286,17 @@
 	</script>
 
 	<tal:block tal:define="global resourcename request/resourcename | request/form/resourceName | nothing" />
+
 	<tal:block tal:condition="resourcename"
 		tal:define="
 			global res python: here.getResourceInfo(modelb, request);
 			global type python: 'tag_name' in res and res['tag_name'] or ''">
 
 	<h2>Configure <span tal:replace="res/name | string: resource" /></h2>
+	<br/>
 
 	<div class="reschoose">
-		<tal:block tal:condition="python: type == 'ip'">
-			<div metal:use-macro="here/resource-form-macros/macros/ip_macro" />
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'fs'">
-			<div metal:use-macro="here/resource-form-macros/macros/fs_macro" />
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'gfs' or type == 'clusterfs'">
-			<div metal:use-macro="here/resource-form-macros/macros/gfs_macro" />
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'nfsm'">
-			<div metal:use-macro="here/resource-form-macros/macros/nfsm_macro"/>
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'nfsx'">
-			<div metal:use-macro="here/resource-form-macros/macros/nfsx_macro"/>
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'nfsc'">
-			<div metal:use-macro="here/resource-form-macros/macros/nfsc_macro"/>
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'smb'">
-			<div metal:use-macro="here/resource-form-macros/macros/smb_macro" />
-		</tal:block>
-
-		<tal:block tal:condition="python: type == 'script'">
-			<div metal:use-macro="here/resource-form-macros/macros/scr_macro" />
-		</tal:block>
+		<tal:block metal:use-macro="here/form-macros/macros/serviceconfig-type-macro" />
 	</div>
 	</tal:block>
 </div>
@@ -353,7 +356,7 @@
 				<input type="checkbox" name="monitorLink"
 					tal:attributes="
 						disabled python: editDisabled;
-						checked res/attrs/monitor_link | string: 1" />
+						checked res/attrs/monitor_link | string: checked" />
 			</td>
 		</tr>
 	</table>
@@ -421,10 +424,10 @@
 
 					<option name="ext3" value="ext3"
 						tal:content="string: ext3"
-						tal:attributes="checked python: fstype == 'ext3' and 'checked'" />
+						tal:attributes="selected python: fstype == 'ext3' and 'selected'" />
 					<option name="ext2" value="ext2"
 						tal:content="string: ext2"
-						tal:attributes="checked python: fstype == 'ext2' and 'checked'" />
+						tal:attributes="selected python: fstype == 'ext2' and 'selected'" />
 				</select>
 			</td>
 		</tr>
@@ -460,7 +463,7 @@
 		</tr>
 
 		<tr class="systemsTable">
-			<td class="systemsTable">File system ID</td>
+			<td class="systemsTable">File system ID (optional)</td>
 			<td class="systemsTable">
 				<input type="text" size="20" name="fsid"
 					tal:attributes="
@@ -585,7 +588,7 @@
 		</tr>
 
 		<tr class="systemsTable">
-			<td class="systemsTable">File system ID</td>
+			<td class="systemsTable">File system ID (optional)</td>
 			<td class="systemsTable">
 				<input type="text" size="20" name="fsid"
 					tal:attributes="
@@ -796,11 +799,569 @@
 				<input type="text" size="20" name="options"
 					tal:attributes="
 						disabled python: editDisabled;
-						value res/attrs/options | nothing"/>
+						value res/attrs/options | nothing" />
+			</td>
+		</tr>
+
+		<tr>
+			<td class="systemsTable">Allow Recover</td>
+			<td class="systemsTable">
+				<input type="checkbox" name="allow_recover"
+					tal:attributes="
+						disabled python: editDisabled;
+						checked res/attrs/allow_recover | nothing" />
+			</td>
+		</tr>
+	</table>
+
+	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
+	</form>
+</div>
+
+<div class="rescfg" name="POSTGRES-8"
+	tal:attributes="id res/name | nothing" metal:define-macro="postgres-8_macro">
+	<p class="reshdr">PostgreSQL 8 Configuration</p>
+
+	<form method="post"
+		tal:attributes="name res/parent_uuid | nothing"
+		tal:define="editDisabled resourceIsRef | nothing">
+
+	<input name="immutable" type="hidden" value="true"
+		tal:condition="editDisabled" />
+
+	<input name="edit" type="hidden" value="true"
+		tal:condition="python: ptype == '33' and True or False" />
+
+	<input name="pagetype" type="hidden"
+		tal:attributes="value python: ptype" />
+
+	<input name="global" type="hidden"
+		tal:attributes="value resourceIsRef | nothing" />
+
+	<input name="parent_uuid" type="hidden"
+		tal:attributes="value res/parent_uuid | nothing" />
+
+	<input name="uuid" type="hidden"
+		tal:attributes="value res/uuid | nothing" />
+
+	<input name="tree_level" type="hidden"
+		tal:attributes="value res/indent_ctr | string:0" />
+
+	<input name="clustername" type="hidden"
+		tal:attributes="
+			value request/clustername | request/form/clustername | nothing" />
+
+	<input name="oldname" type="hidden"
+		tal:attributes="value res/name | nothing" />
+
+	<input name="type" type="hidden" value="postgres-8" />
+
+	<table class="systemsTable">
+		<tr class="systemsTable">
+			<td class="systemsTable">Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="resourceName"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/name | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Config File</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="config_file"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/config_file | string:/var/lib/pgsql/data/postgresql.conf" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Postmaster User</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="postmaster_user"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/postmaster_user | string:postgres" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Postmaster Options</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="postmaster_options"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/postmaster_options | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Shutdown Wait (seconds)</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="shutdown_wait"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/shutdown_wait | string:0" />
 			</td>
 		</tr>
 	</table>
+	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
+	</form>
+</div>
+
+<div class="rescfg" name="APACHE"
+	tal:attributes="id res/name | nothing" metal:define-macro="apache_macro">
+	<p class="reshdr">Apache Configuration</p>
+
+	<form method="post"
+		tal:attributes="name res/parent_uuid | nothing"
+		tal:define="editDisabled resourceIsRef | nothing">
+
+	<input name="immutable" type="hidden" value="true"
+		tal:condition="editDisabled" />
+
+	<input name="edit" type="hidden" value="true"
+		tal:condition="python: ptype == '33' and True or False" />
+
+	<input name="pagetype" type="hidden"
+		tal:attributes="value python: ptype" />
+
+	<input name="global" type="hidden"
+		tal:attributes="value resourceIsRef | nothing" />
+
+	<input name="parent_uuid" type="hidden"
+		tal:attributes="value res/parent_uuid | nothing" />
+
+	<input name="uuid" type="hidden"
+		tal:attributes="value res/uuid | nothing" />
 
+	<input name="tree_level" type="hidden"
+		tal:attributes="value res/indent_ctr | string:0" />
+
+	<input name="clustername" type="hidden"
+		tal:attributes="
+			value request/clustername | request/form/clustername | nothing" />
+
+	<input name="oldname" type="hidden"
+		tal:attributes="value res/name | nothing" />
+
+	<input name="type" type="hidden" value="apache" />
+
+	<table class="systemsTable">
+		<tr class="systemsTable">
+			<td class="systemsTable">Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="resourceName"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/name | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Server Root</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="server_root"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/server_root | string:/etc/httpd" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Config File</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="config_file"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/config_file | string:conf/httpd.conf" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">httpd Options</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="httpd_options"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/httpd_options | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Shutdown Wait (seconds)</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="shutdown_wait"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/shutdown_wait | string:0" />
+			</td>
+		</tr>
+	</table>
+	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
+	</form>
+</div>
+
+<div class="rescfg" name="OPENLDAP"
+	tal:attributes="id res/name | nothing" metal:define-macro="openldap_macro">
+	<p class="reshdr">Open LDAP Configuration</p>
+
+	<form method="post"
+		tal:attributes="name res/parent_uuid | nothing"
+		tal:define="editDisabled resourceIsRef | nothing">
+
+	<input name="immutable" type="hidden" value="true"
+		tal:condition="editDisabled" />
+
+	<input name="edit" type="hidden" value="true"
+		tal:condition="python: ptype == '33' and True or False" />
+
+	<input name="pagetype" type="hidden"
+		tal:attributes="value python: ptype" />
+
+	<input name="global" type="hidden"
+		tal:attributes="value resourceIsRef | nothing" />
+
+	<input name="parent_uuid" type="hidden"
+		tal:attributes="value res/parent_uuid | nothing" />
+
+	<input name="uuid" type="hidden"
+		tal:attributes="value res/uuid | nothing" />
+
+	<input name="tree_level" type="hidden"
+		tal:attributes="value res/indent_ctr | string:0" />
+
+	<input name="clustername" type="hidden"
+		tal:attributes="
+			value request/clustername | request/form/clustername | nothing" />
+
+	<input name="oldname" type="hidden"
+		tal:attributes="value res/name | nothing" />
+
+	<input name="type" type="hidden" value="openldap" />
+
+	<table class="systemsTable">
+		<tr class="systemsTable">
+			<td class="systemsTable">Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="resourceName"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/name | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Config File</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="config_file"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/config_file | string:/etc/openldap/slapd.conf" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">URL List</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="url_list"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/url_list | string:ldap:///" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">slapd Options</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="slapd_options"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/slapd_options | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Shutdown Wait (seconds)</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="shutdown_wait"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/shutdown_wait | string:0" />
+			</td>
+		</tr>
+	</table>
+	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
+	</form>
+</div>
+
+<div class="rescfg" name="LVM"
+	tal:attributes="id res/name | nothing" metal:define-macro="lvm_macro">
+
+	<p class="reshdr">LVM Resource Configuration</p>
+
+	<form method="post"
+		tal:attributes="name res/parent_uuid | nothing"
+		tal:define="editDisabled resourceIsRef | nothing">
+
+	<input name="immutable" type="hidden" value="true"
+		tal:condition="editDisabled" />
+
+	<input name="edit" type="hidden" value="true"
+		tal:condition="python: ptype == '33' and True or False" />
+
+	<input name="pagetype" type="hidden"
+		tal:attributes="value python: ptype" />
+
+	<input name="global" type="hidden"
+		tal:attributes="value resourceIsRef | nothing" />
+
+	<input name="parent_uuid" type="hidden"
+		tal:attributes="value res/parent_uuid | nothing" />
+
+	<input name="uuid" type="hidden"
+		tal:attributes="value res/uuid | nothing" />
+
+	<input name="tree_level" type="hidden"
+		tal:attributes="value res/indent_ctr | string:0" />
+
+	<input name="clustername" type="hidden"
+		tal:attributes="
+			value request/clustername | request/form/clustername | nothing" />
+
+	<input name="oldname" type="hidden"
+		tal:attributes="value res/name | nothing" />
+
+	<input name="type" type="hidden" value="lvm" />
+
+	<table class="systemsTable">
+		<tr class="systemsTable">
+			<td class="systemsTable">Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="resourceName"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/name | nothing" />
+			</td>
+		</tr>
+		<tr class="systemsTable">
+			<td class="systemsTable">Volume Group Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="vg_name"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/vg_name | nothing" />
+			</td>
+		</tr>
+		<tr class="systemsTable">
+			<td class="systemsTable">Logical Volume Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="lv_name"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/lv_name | nothing" />
+			</td>
+		</tr>
+	</table>
+
+	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
+	</form>
+</div>
+
+<div class="rescfg" name="MYSQL"
+	tal:attributes="id res/name | nothing" metal:define-macro="mysql_macro">
+	<p class="reshdr">MySQL Configuration</p>
+
+	<form method="post"
+		tal:attributes="name res/parent_uuid | nothing"
+		tal:define="editDisabled resourceIsRef | nothing">
+
+	<input name="immutable" type="hidden" value="true"
+		tal:condition="editDisabled" />
+
+	<input name="edit" type="hidden" value="true"
+		tal:condition="python: ptype == '33' and True or False" />
+
+	<input name="pagetype" type="hidden"
+		tal:attributes="value python: ptype" />
+
+	<input name="global" type="hidden"
+		tal:attributes="value resourceIsRef | nothing" />
+
+	<input name="parent_uuid" type="hidden"
+		tal:attributes="value res/parent_uuid | nothing" />
+
+	<input name="uuid" type="hidden"
+		tal:attributes="value res/uuid | nothing" />
+
+	<input name="tree_level" type="hidden"
+		tal:attributes="value res/indent_ctr | string:0" />
+
+	<input name="clustername" type="hidden"
+		tal:attributes="
+			value request/clustername | request/form/clustername | nothing" />
+
+	<input name="oldname" type="hidden"
+		tal:attributes="value res/name | nothing" />
+
+	<input name="type" type="hidden" value="mysql" />
+
+	<table class="systemsTable">
+		<tr class="systemsTable">
+			<td class="systemsTable">Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="resourceName"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/name | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Config File</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="config_file"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/config_file | string:/etc/my.cnf" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Listen Address</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="listen_address"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/listen_address | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">mysqld Options</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="mysqld_options"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/mysqld_options | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Shutdown Wait (seconds)</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="shutdown_wait"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/shutdown_wait | string:0" />
+			</td>
+		</tr>
+	</table>
+	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
+	</form>
+</div>
+
+<div class="rescfg" name="TOMCAT-5"
+	tal:attributes="id res/name | nothing" metal:define-macro="tomcat-5_macro">
+	<p class="reshdr">Tomcat 5 Configuration</p>
+
+	<form method="post"
+		tal:attributes="name res/parent_uuid | nothing"
+		tal:define="editDisabled resourceIsRef | nothing">
+
+	<input name="immutable" type="hidden" value="true"
+		tal:condition="editDisabled" />
+
+	<input name="edit" type="hidden" value="true"
+		tal:condition="python: ptype == '33' and True or False" />
+
+	<input name="pagetype" type="hidden"
+		tal:attributes="value python: ptype" />
+
+	<input name="global" type="hidden"
+		tal:attributes="value resourceIsRef | nothing" />
+
+	<input name="parent_uuid" type="hidden"
+		tal:attributes="value res/parent_uuid | nothing" />
+
+	<input name="uuid" type="hidden"
+		tal:attributes="value res/uuid | nothing" />
+
+	<input name="tree_level" type="hidden"
+		tal:attributes="value res/indent_ctr | string:0" />
+
+	<input name="clustername" type="hidden"
+		tal:attributes="
+			value request/clustername | request/form/clustername | nothing" />
+
+	<input name="oldname" type="hidden"
+		tal:attributes="value res/name | nothing" />
+
+	<input name="type" type="hidden" value="tomcat-5" />
+
+	<table class="systemsTable">
+		<tr class="systemsTable">
+			<td class="systemsTable">Name</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="resourceName"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/name | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Config File</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="config_file"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/config_file | string:/etc/tomcat5/tomcat5.conf" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Tomcat User</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="tomcat_user"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/tomcat_user | string:tomcat" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Catalina Options</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="catalina_options"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/catalina_options | nothing" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Catalina Base</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="catalina_base"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/catalina_base | string:/usr/share/tomcat5" />
+			</td>
+		</tr>
+
+		<tr class="systemsTable">
+			<td class="systemsTable">Shutdown Wait (seconds)</td>
+			<td class="systemsTable">
+				<input type="text" size="20" name="shutdown_wait"
+					tal:attributes="
+						disabled python: editDisabled;
+						value res/attrs/shutdown_wait | string:30" />
+			</td>
+		</tr>
+	</table>
 	<div metal:use-macro="here/resource-form-macros/macros/res_form_footer" />
 	</form>
 </div>
--- conga/luci/cluster/resource_form_handlers.js	2006/12/07 17:54:31	1.20.2.3
+++ conga/luci/cluster/resource_form_handlers.js	2007/04/12 17:19:18	1.20.2.3.2.1
@@ -69,8 +69,10 @@
 	var divs = container.getElementsByTagName('div');
 
 	for (var i = 0 ; i < divs.length ; i++) {
-		if (!swap_in_elem && divs[i].getAttribute('name') == swap_in_name)
+		if (!swap_in_elem && divs[i].getAttribute('name') == swap_in_name) {
 			swap_in_elem = divs[i];
+			break;
+		}
 	}
 	if (!swap_in_elem)
 		return (-1);
@@ -98,6 +100,41 @@
 	return (errors);
 }
 
+function validate_apache(form) {
+	var errors = new Array();
+	return (errors);
+}
+
+function validate_tomcat5(form) {
+	var errors = new Array();
+	return (errors);
+}
+
+function validate_postgres8(form) {
+	var errors = new Array();
+	return (errors);
+}
+
+function validate_mysql(form) {
+	var errors = new Array();
+	return (errors);
+}
+
+function validate_lvm(form) {
+	var errors = new Array();
+	return (errors);
+}
+
+function validate_openldap(form) {
+	var errors = new Array();
+	return (errors);
+}
+
+function validate_nfs_export(form) {
+	var errors = new Array();
+	return (errors);
+}
+
 function validate_nfs_mount(form) {
 	var errors = new Array();
 
@@ -121,11 +158,6 @@
 	return (errors);
 }
 
-function validate_nfs_export(form) {
-	var errors = new Array();
-	return (errors);
-}
-
 function validate_nfs_client(form) {
 	var errors = new Array();
 
@@ -158,11 +190,6 @@
 	} else
 		clr_form_err(form.device);
 
-	if (!form.fsid || str_is_blank(form.fsid.value)) {
-		errors.push('No file system ID was given.');
-		set_form_err(form.fsid);
-	} else
-		clr_form_err(form.fsid);
 	return (errors);
 }
 
@@ -181,11 +208,6 @@
 	} else
 		clr_form_err(form.device);
 
-	if (!form.fsid || str_is_blank(form.fsid.value)) {
-		errors.push('No file system ID was given.');
-		set_form_err(form.fsid);
-	} else
-		clr_form_err(form.fsid);
 	return (errors);
 }
 
@@ -211,6 +233,14 @@
 	return (errors);
 }
 
+var required_children = new Array();
+required_children['nfsx'] = [ 'nfsc' ];
+
+var forbidden_children = new Array();
+forbidden_children['ip'] = [ 'nfsc', 'nfsx' ];
+forbidden_children['netfs'] = [ 'nfsc', 'nfsx' ];
+forbidden_children['nfsx'] = [ 'nfsx' ];
+
 var form_validators = new Array();
 form_validators['ip'] = validate_ip;
 form_validators['nfsm'] = validate_nfs_mount;
@@ -220,6 +250,12 @@
 form_validators['gfs'] = validate_gfs;
 form_validators['scr'] = validate_script;
 form_validators['smb'] = validate_samba;
+form_validators['apache'] = validate_apache;
+form_validators['tomcat-5'] = validate_tomcat5;
+form_validators['postgres-8'] = validate_postgres8;
+form_validators['openldap'] = validate_openldap;
+form_validators['mysql'] = validate_mysql;
+form_validators['lvm'] = validate_lvm;
 
 function check_form(form) {
 	var valfn = form_validators[form.type.value];
@@ -241,7 +277,10 @@
 	var errors = check_form(form);
 	if (error_dialog(errors))
 		return (-1);
-	form.submit();
+
+	var confirm_str = form.edit ? 'Update this resource?' : 'Add this resource?';
+	if (confirm(confirm_str))
+		form.submit();
 }
 
 function delete_resource(form) {
@@ -382,6 +421,9 @@
 	var form_xml = '';
 	var svc_name = null;
 	var autostart = 1;
+	var domain = null;
+	var exclusive = 0;
+	var recovery = null;
 
 	var form = document.getElementsByTagName('form');
 	for (var i = 0 ; i < form.length ; i++) {
@@ -394,8 +436,26 @@
 				clr_form_err(form[i].service_name);
 				svc_name = form[i].service_name.value;
 			}
+
 			if (!form[i].autostart.checked)
 				autostart = 0;
+
+			if (!form[i].exclusive.checked)
+				exclusive = 0;
+			else
+				exclusive = 1;
+
+			if (form[i].recovery) {
+				recovery = form[i].recovery.options[form[i].recovery.options.selectedIndex].value;
+				if (str_is_blank(recovery))
+					recovery = null;
+			}
+
+			if (form[i].domain) {
+				domain = form[i].domain.options[form[i].domain.options.selectedIndex].value;
+				if (str_is_blank(domain))
+					domain = null;
+			}
 			continue;
 		}
 
@@ -415,11 +475,12 @@
 			{
 				temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '" value="' + input_elem[j].value + '" />';
 			} else if (res_type == 'checkbox' || res_type == 'radio') {
-				if (input_elem[j].checked)
+				if (input_elem[j].checked) {
 					temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '" checked="checked"';
-				if (res_type == 'radio')
-					temp += ' value="' + input_elem[j].value + '"';
-				temp += ' />';
+					if (res_type == 'radio')
+						temp += ' value="' + input_elem[j].value + '"';
+					temp += ' />';
+				}
 			}
 		}
 
@@ -438,13 +499,22 @@
 	if (!form_xml)
 		errors.push('No resource information was submitted.');
 
+	if (recovery && recovery != 'relocate' && recovery != 'restart' && recovery != 'disable') {
+		errors.push('You entered an invalid recovery option. Valid options are "relocate" "restart" and "disable"');
+	}
+
 	if (error_dialog(errors))
 		return (-1);
 
 	/* sort this out in the backend */
 	master_form.form_xml.value = '<formlist>' + form_xml + '</formlist>';
 	master_form.svc_name.value = svc_name;
+	if (domain)
+		master_form.domain.value = domain;
+	if (recovery)
+		master_form.recovery.value = recovery;
 	master_form.autostart.value = autostart;
+	master_form.exclusive.value = exclusive;
 
 	var confirm_msg = null;
 	if (master_form.action.value == 'add')
--- conga/luci/cluster/validate_config_general.js	2006/10/04 17:24:58	1.3
+++ conga/luci/cluster/validate_config_general.js	2007/04/12 17:19:18	1.3.4.1
@@ -8,7 +8,11 @@
 		set_form_err(form.cluname);
 	} else {
 		name = form.cluname.value;
-		clr_form_err(form.cluname);
+		if (name.length > 15) {
+			errors.push('A cluster\'s name must be less than 16 characters long.');
+			set_form_err(form.cluname);
+		} else
+			clr_form_err(form.cluname);
 	}
 
 	if (!form.cfgver || str_is_blank(form.cfgver.value)) {
--- conga/luci/cluster/validate_config_qdisk.js	2006/12/22 17:50:16	1.4.2.1
+++ conga/luci/cluster/validate_config_qdisk.js	2007/04/12 17:19:18	1.4.2.1.2.1
@@ -1,4 +1,4 @@
-var heuristic_names = [ ':hname', ':hprog', ':hinterval', ':hscore', ':hdel' ];
+var heuristic_names = [ ':hprog', ':hinterval', ':hscore', ':hdel' ];
 
 function clear_heuristic(form, heur_num) {
 	var str_prefix = 'heuristic' + heur_num;
@@ -82,26 +82,14 @@
 
 	var hstr = 'heuristic' + hnum;
 
-	var hname = document.getElementById(hstr + ':hname');
-	if (!hname || str_is_blank(hname.value)) {
-		++blank;
-		errors.push('No name was given for heuristic ' + (hnum + 1));
-		set_form_err(hname);
-	} else {
-		/* XXX sanity check the name */
-		hname = hname.value;
-		clr_form_err(hname);
-	}
-
-	var hpath = document.getElementById(hstr + ':hpath');
-	if (!hpath || str_is_blank(hpath.value)) {
+	var hprog = document.getElementById(hstr + ':hprog');
+	if (!hprog || str_is_blank(hprog.value)) {
 		++blank;
 		errors.push('No path was given for heuristic ' + (hnum + 1));
-		set_form_err(hpath);
+		set_form_err(hprog);
 	} else {
 		/* XXX sanity check the path */
-		hpath = hpath.value;
-		clr_form_err(hpath);
+		clr_form_err(hprog);
 	}
 
 	var hint = document.getElementById(hstr + ':hinterval');
@@ -110,8 +98,7 @@
 		errors.push('No interval was given for heuristic ' + (hnum + 1));
 		set_form_err(hint);
 	} else {
-		hint = hint.value;
-		if (!is_valid_int(hint, 1, null)) {
+		if (!is_valid_int(hint.value, 1, null)) {
 			errors.push('Heuristic interval values must be greater than 0.');
 			set_form_err(hint);
 		} else
@@ -124,18 +111,16 @@
 		errors.push('No score was given for heuristic ' + (hnum + 1));
 		set_form_err(hscore);
 	} else {
-		hscore = hscore.value;
-		if (!is_valid_int(hscore, 0, null)) {
+		if (!is_valid_int(hscore.value, 0, null)) {
 			errors.push('Heuristic score values must be 0 or greater.');
 			set_form_err(hscore);
 		} else
 			clr_form_err(hscore);
 	}
 
-	if (blank == 4) {
+	if (blank == 3) {
 		/* The entry is blank -- ignore it. */
-		clr_form_err(hname);
-		clr_form_err(hpath);
+		clr_form_err(hprog);
 		clr_form_err(hint);
 		clr_form_err(hscore);
 		return (null);
@@ -219,30 +204,15 @@
 				clr_form_err(form.min_score);
 		}
 
-		if (!form.device || str_is_blank(form.device.value)) {
-			errors.push('No device setting was given.');
-			set_form_err(form.device);
-		} else {
-			/* TODO: check this */
-			var device = form.device.value;
-			clr_form_err(form.device);
-		}
-
-		if (!form.label || str_is_blank(form.label.value)) {
-			errors.push('No label setting was given.');
-			set_form_err(form.label);
-		} else {
-			/* TODO: check this */
-			var label = form.device.label;
-			clr_form_err(form.label);
-		}
+		var no_dev = !form.device || str_is_blank(form.device.value);
+		var no_label = !form.label || str_is_blank(form.label.value);
+		if (no_dev && no_label)
+			errors.push('You must give either a label or a device.');
 
 		var hnum = document.getElementById('num_heuristics');
 		if (hnum) {
-			hnum = Number(hnum.value);
-			if (hnum === 0)
-				hnum++;
-			for (var i = 0 ; i < hnum ; i++) {
+			hnum = Number(hnum.value) + 1;
+			for (var i = 0 ; i <= hnum ; i++) {
 				var err = check_heuristic(i, form);
 				if (err)
 					errors = errors.concat(err);
@@ -252,6 +222,7 @@
 
 	if (error_dialog(errors))
 		return (-1);
+
 	if (confirm('Update quorum partition properties?'))
 		form.submit();
 }
@@ -275,15 +246,6 @@
 
 	var hstr = 'heuristic' + cur_hnum;
 
-	var name_td = document.createElement('td');
-	name_td.className = 'systemsTable';
-	var name_input = document.createElement('input');
-	name_input.className = 'qdname qdisk';
-	name_input.setAttribute('name', hstr + ':hname');
-	name_input.setAttribute('id', hstr + ':hname');
-	name_input.setAttribute('type', 'text');
-	name_td.appendChild(name_input);
-
 	var path_td = document.createElement('td');
 	path_td.className = 'systemsTable';
 	var path_input = document.createElement('input');
@@ -325,7 +287,6 @@
 	var tr = document.createElement('tr');
 	tr.className = 'systemsTable';
 	tr.setAttribute('id', 'heuristic' + cur_hnum);
-	tr.appendChild(name_td);
 	tr.appendChild(path_td);
 	tr.appendChild(interval_td);
 	tr.appendChild(score_td);
--- conga/luci/cluster/validate_fence.js	2006/10/04 17:25:20	1.1
+++ conga/luci/cluster/validate_fence.js	2007/04/12 17:19:18	1.1.4.1
@@ -1,48 +1,251 @@
-function validate_fence_apc(form) {
-	return (0);
-}
+var fence_inst_validator = new Array();
+fence_inst_validator['apc'] = [ 'port', 'switch' ];
+fence_inst_validator['bladecenter'] = [ 'blade' ];
+fence_inst_validator['brocade'] = [ 'port' ];
+fence_inst_validator['bullpap'] = [ 'domain' ];
+fence_inst_validator['egenera'] = [ 'lpan', 'pserver' ];
+fence_inst_validator['gndb'] = [ 'ipaddress' ];
+fence_inst_validator['mcdata'] = [ 'port' ];
+fence_inst_validator['sanbox2'] = [ 'port' ];
+fence_inst_validator['scsi'] = [ 'nodename' ];
+fence_inst_validator['vixel'] = [ 'port' ];
+fence_inst_validator['wti'] = [ 'port' ];
+fence_inst_validator['xvm'] = [ 'domain' ];
+
+var fence_validator = new Array();
+fence_validator['apc'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['bladecenter'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['brocade'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['bullpap'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['drac'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['egenera'] = [ 'cserver' ];
+fence_validator['gnbd'] = [ 'servers' ];
+fence_validator['ilo'] = [ 'hostname', 'login', 'passwd', 'passwd_script' ];
+fence_validator['ipmilan'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script', 'lanplus', 'auth' ];
+fence_validator['manual'] = [];
+fence_validator['mcdata'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['rps10'] = [ 'device', 'port'];
+fence_validator['rsa'] = [ 'hostname', 'login', 'passwd', 'passwd_script' ];
+fence_validator['sanbox2'] = [ 'ipaddr', 'login', 'passwd', 'passwd_script' ];
+fence_validator['scsi'] = [];
+fence_validator['unknown'] = [];
+fence_validator['vixel'] = [ 'ipaddr', 'passwd', 'passwd_script' ];
+fence_validator['wti'] = [ 'ipaddr', 'passwd', 'passwd_script' ];
+fence_validator['xvm'] = [];
+
+function validate_field_str(form, form_elem) {
+	var errors = new Array();
+
+	if (!form_elem || str_is_blank(form_elem.value)) {
+		if (!form_elem)
+			errors.push('No value was given for this field.');
+		else {
+			set_form_err(form_elem);
+			errors.push(form_elem.name + ' values must not be blank.');
+		}
+		return (errors);
+	}
 
-function validate_fence_wti(form) {
-	return (0);
+	clr_form_err(form_elem);
+	return (null);
 }
 
-function validate_fence_rsa(form) {
-	return (0);
+function validate_field_passwd(form, form_elem) {
+	if (form_elem.disabled) {
+		clr_form_err(form_elem);
+		return (null);
+	}
+
+	var errors = validate_field_str(form, form_elem);
+	if (errors && errors.length > 0 && form.passwd_script && !str_is_blank(form.passwd_script.value))
+	{
+		clr_form_err(form_elem);
+		return (null);
+	}
+	return errors;
+}
+
+/* Very loose checking for now -- just make sure it's not blank */
+function validate_field_host(form, form_elem) {
+	return (validate_field_str(form, form_elem));
+}
+
+function validate_field_noop(form, form_elem) {
+	if (form_elem)
+		clr_form_err(form_elem);
+	return (null);
 }
 
-function validate_fence_ilo(form) {
-	return (0);
+function validate_field_ipmilan_auth(form, form_elem) {
+	var errors = new Array();
+
+	if (!form_elem || str_is_blank(form_elem.value))
+		return (null);
+
+	var auth_type = form_elem.value;
+	if (auth_type != 'none' && auth_type != 'password' && auth_type != 'md5') {
+		errors.push('Auth type must be either \'none\' (or blank), \'md5\' or \'password\'');
+		set_form_err(form_elem);
+		return (errors);
+	}
+
+	clr_form_err(form_elem);
+	return (null);
 }
 
-function validate_fence_drac(form) {
-	return (0);
+var field_validator = new Array();
+field_validator['ipaddr'] = validate_field_host;
+field_validator['hostname'] = validate_field_host;
+field_validator['login'] = validate_field_str;
+field_validator['passwd'] = validate_field_passwd;
+field_validator['servers'] = validate_field_str;
+field_validator['cserver'] = validate_field_str;
+field_validator['device'] = validate_field_str;
+field_validator['port'] = validate_field_str;
+field_validator['passwd_script'] = validate_field_noop;
+
+/* IPMI fence device */
+field_validator['lanplus'] = validate_field_noop;
+field_validator['auth'] = validate_field_ipmilan_auth;
+
+/* APC instance switch */
+field_validator['switch'] = validate_field_noop;
+
+field_validator['ipaddress'] = validate_field_host;
+field_validator['blade'] = validate_field_str;
+field_validator['nodename'] = validate_field_host;
+field_validator['lpan'] = validate_field_str;
+field_validator['pserver'] = validate_field_str;
+field_validator['domain'] = validate_field_str;
+
+function validate_fence(form) {
+	var errors = new Array();
+
+	if (!form.fence_type || str_is_blank(form.fence_type.value))
+		errors.push('No fence device was selected.');
+
+	if (errors.length > 0)
+		return (errors);
+
+	if (!form.name || str_is_blank(form.name.value)) {
+		errors.push('A unique name must be given for all fence devices.');
+		set_form_err(form.name);
+	} else
+		clr_form_err(form.name);
+
+	if (errors.length > 0)
+		return (errors);
+
+	var fence_type = form.fence_type.value.replace(/^fence_/, '');
+	var fields = fence_validator[fence_type];
+	if (!fields)
+		errors.push('An unknown fence device type was given: \"' + fence_type + '.\"');
+
+	if (errors.length > 0)
+		return (errors);
+
+	for (var i = 0 ; i < fields.length ; i++) {
+		var field_name = fields[i];
+		if (form[field_name]) {
+			var err = field_validator[field_name](form, form[field_name]);
+			if (err)
+				errors = errors.concat(err);
+		}
+	}
+
+	return (errors);
 }
 
-function validate_fence_ipmilan(form) {
-	return (0);
+function validate_fence_form(form) {
+	var errors = validate_fence(form);
+
+	if (error_dialog(errors))
+		return (-1);
+
+	if (confirm('Update fence device properties?'))
+		form.submit();
 }
 
-var fence_validator = new Object();
-fence_validator['apc'] = validate_fence_apc;
-fence_validator['wti'] = validate_fence_wti;
-fence_validator['rsa'] = validate_fence_rsa;
-fence_validator['ilo'] = validate_fence_ilo;
-fence_validator['drac'] = validate_fence_drac;
-fence_validator['ipmilan'] = validate_fence_ipmilan;
+function validate_fence_instance(form) {
+	var errors = new Array();
 
-function validate_fence_common(form) {
-	return (null);
+	if (!form.fence_type || str_is_blank(form.fence_type.value))
+		errors.push('The fence device associated with this instance could not be determined.');
+
+	if (errors.length > 0)
+		return (errors);
+
+	var fence_type = form.fence_type.value.replace(/^fence_/, '');
+	var fields = fence_inst_validator[fence_type];
+	if (!fields)
+		errors.push('An unknown fence device type was given: \"' + fence_type + '.\"');
+
+	if (errors.length > 0)
+		return (errors);
+
+	for (var i = 0 ; i < fields.length ; i++) {
+		var field_name = fields[i];
+		if (form[field_name]) {
+			var err = field_validator[field_name](form, form[field_name]);
+			if (err)
+				errors = errors.concat(err);
+		}
+	}
+
+	return (errors);
 }
 
-function validate_fence(form) {
-	if (!form || !form.fence_type || !form.fence_type.value)
+function validate_node_fence_form(master_form, container_id) {
+	var errors = new Array();
+	var div_elem = document.getElementById(container_id);
+	if (!div_elem)
 		return (-1);
-	var errors = validate_fence_common(form);
-	var f = fence_validator[form.fence_type.value];
-	if (!f)
+	var form_xml = '';
+
+	var form = div_elem.getElementsByTagName('form');
+	for (var i = 0 ; i < form.length ; i++) {
+		var err = null;
+		if (form[i].fence_instance)
+			err = validate_fence_instance(form[i]);
+		else if (form[i].fence_type)
+			err = validate_fence(form[i]);
+		if (err)
+			errors = errors.concat(err);
+		var input_elem = form[i].getElementsByTagName('input');
+		var temp = '';
+		for (var j = 0 ; j < input_elem.length ; j++) {
+			var res_type = input_elem[j].type;
+			if (res_type == 'hidden' || res_type == 'text' ||
+				res_type == 'password')
+			{
+				temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '" value="' + input_elem[j].value + '" />';
+			} else if (res_type == 'checkbox' || res_type == 'radio') {
+				if (input_elem[j].checked) {
+					temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '"';
+					if (res_type == 'checkbox')
+						temp += ' value="1"';
+					else if (res_type == 'radio')
+						temp += ' value="' + input_elem[j].value + '"';
+					temp += ' />';
+				} else if (res_type == 'checkbox') {
+					temp += '<input type="' + res_type + '" name="' + input_elem[j].name + '" value="0" />';
+				}
+			}
+		}
+
+		var select_elem = form[i].getElementsByTagName('select');
+		for (var j = 0 ; j < select_elem.length ; j++) {
+			temp += '<input type="text" name="' + select_elem[j].name + '" value="' + select_elem[j].options[select_elem[j].options.selectedIndex].value + '" />';
+		}
+
+		form_xml += '<form id="' + form[i].getAttribute('name') + '">' + temp + '</form>';
+	}
+
+	master_form.fence_xml.value = '<formlist>' + form_xml + '</formlist>';
+
+	if (error_dialog(errors))
 		return (-1);
-	var err = f(form);
-	if (err)
-		errors = errors.concat(err);
-	return (errors);
+
+	if (confirm('Update this node\'s fence configuration?'))
+		master_form.submit();
 }
--- conga/luci/conga_ssl/SSLClient.cpp	2006/12/22 17:50:16	1.1.2.2
+++ conga/luci/conga_ssl/SSLClient.cpp	2007/04/12 17:19:18	1.1.2.2.2.1
@@ -81,14 +81,14 @@
   if (!SSL_CTX_load_verify_locations(ctx, 
 				     _trust_CAs, 
 				     NULL))
-    cout << "failed to load trusted CAs" << endl;
+    ;//cout << "failed to load trusted CAs" << endl;
   
   STACK_OF(X509_NAME) *cert_names = 
     SSL_load_client_CA_file(_trust_CAs);
   if (cert_names)
     SSL_CTX_set_client_CA_list(ctx, cert_names);
-  else
-    cout << "failed to load trusted CAs" << endl;
+//  else
+//    cout << "failed to load trusted CAs" << endl;
   
   // load saved certs
   
--- conga/luci/conga_ssl/conga_ssl_lib.cpp	2006/12/22 17:50:16	1.1.2.2
+++ conga/luci/conga_ssl/conga_ssl_lib.cpp	2007/04/12 17:19:18	1.1.2.2.2.1
@@ -133,7 +133,7 @@
     counting_auto_ptr<SSLClient> ss;
     {
       PythonThreadsAllower all;
-      ClientSocket sock(hostname, port);
+      ClientSocket sock(hostname, port, timeout * 1000);
       ss = counting_auto_ptr<SSLClient>(new SSLClient(sock));
       ss->connect(timeout * 1000);
     }
--- conga/luci/conga_ssl/setup.py	2006/12/08 18:27:32	1.1.2.1
+++ conga/luci/conga_ssl/setup.py	2007/04/12 17:19:18	1.1.2.1.2.1
@@ -13,6 +13,7 @@
                                'SSLClient.cpp', 
                                '../../ricci/common/ClientSocket.cpp', 
                                '../../ricci/common/Socket.cpp', 
+                               '../../ricci/common/Network.cpp', 
                                '../../ricci/common/Logger.cpp', 
                                '../../ricci/common/Time.cpp', 
                                '../../ricci/common/File.cpp', 
--- conga/luci/homebase/form-macros	2007/01/04 00:22:13	1.44.2.7
+++ conga/luci/homebase/form-macros	2007/04/12 17:19:18	1.44.2.7.2.1
@@ -199,13 +199,15 @@
 		<span tal:condition="python:perms" tal:content="string:Select a User" /><br/>
 
 		<select tal:omit-tag="python: not perms" class="homebase" name="userList" onChange="document.location = this.form.baseURL.value + '&user=' + this.form.userList.options[this.form.userList.selectedIndex].text">
-			<tal:block tal:repeat="user python:perms">
+			<tal:block tal:define="userlist python: perms.keys().sort()">
+			<tal:block tal:repeat="user userlist">
 				<option class="homebase"
 					tal:content="python:user"
 					tal:attributes="value python:user;
 									selected python:user == curUser"
 				/>
 			</tal:block>
+			</tal:block>
 		</select>
 
 		<h3 class="homebase" tal:condition="python:systems[0] and len(systems[0]) > 0">
@@ -221,7 +223,7 @@
 					tal:define="global num_clusters python:num_clusters + 1"
 					tal:attributes="
 						checked python:perms[curUser]['cluster'][c] and 'checked' or None;
-						name python:'__CLUSTER:' + cfname;
+						name python:'__CLUSTER:X___' + cfname;
 						id python:'__CLUSTER' + str(num_clusters);
 						value python: c"
 				/>
@@ -237,7 +239,7 @@
 					tal:define="global num_systems python:num_systems + 1"
 					tal:attributes="
 						checked python:perms[curUser]['storage'][s] and 'checked' or None;
-						name python:'__SYSTEM:' + here.strFilter('[^0-9A-Za-z_-]', '_', s);
+						name python:'__SYSTEM:X___' + here.strFilter('[^0-9A-Za-z_-]', '_', s);
 						id python:'__SYSTEM' + str(num_systems);
 						value python:s"
 				/>
@@ -293,9 +295,11 @@
 
 	<h2 class="homebase">Manage Systems and Clusters</h2>
 
-	<h3>Reauthenticate to Storage or Cluster Systems</h3>
 
-	<form name="authform" method="post" action="">
+	<form name="authform" method="post" action="" class="form_border">
+		<fieldset>
+			<legend>Reauthenticate</legend>
+		<h3>Reauthenticate to Storage or Cluster Systems</h3><br/>
 		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0"
 			tal:define="
 				new_systems request/SESSION/auth_systems | nothing;
@@ -369,7 +373,7 @@
 								tal:attributes="
 									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
 									name python: '__SYSTEM%d:Passwd' % cur_sysnum;
-									value sys/passwd | nothing" />
+									value nothing" />
 						</td>
 						<td class="systemsTable">
 							<img 
@@ -409,7 +413,10 @@
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<ul class="vanilla">
-						<li class="vanilla"><input name="check_certs" type="checkbox">View system certificates before sending any passwords.</li>
+						<li class="vanilla">
+							<input name="check_certs" id="view_certs"
+								onchange="view_certs_only(this.form, this.checked)"
+								type="checkbox">View system certificates before sending any passwords.</li>
 						<li class="vanilla"
 							tal:attributes="id python: cur_sysnum < 2 and 'allSameDiv' or ''">
 							<input type="checkbox" name="allSameCheckBox"
@@ -467,6 +474,7 @@
 				tal:define="x python: request.SESSION.delete('auth_status')" />
 			<div class="padding"> </div>
 		</tal:block>
+	</fieldset>
 	</form>
 
 	<tal:block tal:define="
@@ -475,8 +483,10 @@
 		global num_clusters python:-1;
 		global num_systems python:-1" />
 
-	<form name="adminform" method="post" action=""
+	<form name="adminform" method="post" class="form_border" action=""
 		tal:condition="python:(systems[0] and len(systems[0]) > 0) or (systems[1] and len(systems[1]) > 0)">
+	<fieldset>
+		<legend>Remove clusters and systems</legend>
 
 		<tal:block tal:define="global blankForm python:0" />
 
@@ -497,13 +507,13 @@
 				<input type="checkbox" class="homebase"
 					tal:define="global num_clusters python:num_clusters + 1"
 					tal:attributes="
-						name python:'__CLUSTER:' + cfname;
+						name python:'__CLUSTER:X___' + cfname;
 						id python:'__CLUSTER' + str(num_clusters);
 						value python: c"
 				/>
 				<input type="hidden" value=""
 					tal:attributes="
-						name python:'__CLUSTER:' + cfname"
+						name python:'__CLUSTER:X___' + cfname"
 				/>
 				<span tal:omit-tag="" tal:content="python:c" />
 			</div>
@@ -518,13 +528,13 @@
 					<input type="checkbox" class="homebase"
 						tal:define="global num_systems python:num_systems + 1"
 						tal:attributes="
-							name python:'__SYSTEM:' + sfname;
+							name python:'__SYSTEM:X___' + sfname;
 							id python:'__SYSTEM' + str(num_systems);
 							value python:s"
 					/>
 
 					<input type="hidden" value=""
-						tal:attributes="name python:'__SYSTEM:' + sfname" />
+						tal:attributes="name python:'__SYSTEM:X___' + sfname" />
 					<span class="hbText" tal:omit-tag="" tal:content="python:s"/>
 				</div>
 			</tal:block>
@@ -540,6 +550,7 @@
 			<input type="button" name="Submit" value="Remove selected entries"
 				onClick="validateForm(this.form)" />
 		</div>
+	</fieldset>
 	</form>
 
 	<div tal:condition="python: blankForm">
@@ -658,7 +669,7 @@
 								tal:attributes="
 									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
 									name python: '__SYSTEM%d:Passwd' % cur_sysnum;
-									value sys/passwd | nothing" />
+									value nothing" />
 									
 						</td>
 						<td class="systemsTable">
@@ -699,7 +710,10 @@
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<ul class="vanilla">
-						<li class="vanilla"><input name="check_certs" type="checkbox">View system certificates before sending any passwords.</li>
+						<li class="vanilla">
+							<input name="check_certs" id="view_certs"
+								onchange="view_certs_only(this.form, this.checked)"
+								type="checkbox">View system certificates before sending any passwords.</li>
 						<li class="vanilla"
 							tal:attributes="id python: cur_sysnum < 2 and 'allSameDiv' or ''"><input type="checkbox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(this.form);"/>Check if storage system passwords are identical.</li>
 					</ul>
@@ -796,7 +810,8 @@
 					<ul class="vanilla">
 						<li class="vanilla">
 							<input name="check_certs" type="checkbox"
-								tal:attributes="checked python: add_cluster['check_certs'] and 'checked'" />
+								id="view_certs"
+								onchange="view_certs_only(this.form, this.checked)" />
 							View system certificates before sending any passwords.
 						</li>
 						<li class="vanilla" id="allSameDiv">
@@ -840,7 +855,7 @@
 								autocomplete="off"
 								onChange="pwd0Change(this.form)"
 								tal:attributes="
-									value sys/passwd | nothing;
+									value nothing;
 									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
 									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
 									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
@@ -948,8 +963,11 @@
 						tal:condition="cur_sys" />
 
 					<ul class="vanilla">
-						<li class="vanilla"><input name="check_certs" type="checkbox">View system certificates before sending any passwords.</li>
-						<li class="vanilla"><input type="checkbox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(this.form);"/>Authenticate to all cluster nodes using the password provided above.</li>
+						<li class="vanilla">
+							<input name="check_certs" id="view_certs"
+								onchange="view_certs_only(this.form, this.checked)"
+								type="checkbox">View system certificates before sending any passwords.
+						</li>
 					</ul>
 				</td></tr>
 			</tfoot>
@@ -968,7 +986,7 @@
 							autocomplete="off"
 							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd"
 							tal:attributes="
-								value cur_sys/passwd | nothing" />
+								value nothing" />
 					</td>
 					<tal:block tal:condition="cur_sys">
 						<td class="systemsTable">
--- conga/luci/homebase/homebase_common.js	2006/12/22 17:50:16	1.13.2.2
+++ conga/luci/homebase/homebase_common.js	2007/04/12 17:19:18	1.13.2.2.2.1
@@ -5,7 +5,7 @@
 
 function clr_form_err(ielem) {
 	if (ielem)
-		ielem.className = ielem.className.replace(/ formerror/, '');
+		ielem.className = ielem.className.replace(/( )?formerror/, '');
 }
 
 function toggle_visible(img_obj, elem_id, label_id) {
@@ -132,6 +132,26 @@
 	return (null);
 }
 
+function view_certs_only(form, state) {
+	var num_systems = form.numStorage.value;
+	if (!form.numStorage)
+		return (-1);
+
+	if (state) {
+		var cb = document.getElementById('allSameCheckBox');
+		if (cb && cb.checked)
+			cb.checked = false;
+	}
+
+	for (var i = 0 ; i < num_systems ; i++) {
+		var passwd = document.getElementById('__SYSTEM' + i + ':Passwd');
+		if (passwd) {
+			passwd.value = "";
+			passwd.disabled = state;
+		}
+	}
+}
+
 function allPasswdsSame(form) {
 	var cb = document.getElementById('allSameCheckBox');
 	if (!cb)
@@ -252,6 +272,12 @@
 	var added_storage = new Array();
 	var num_systems = form.numStorage.value;
 
+	var view_certs = document.getElementById('view_certs');
+	if (view_certs)
+		view_certs = view_certs.checked;
+	else
+		view_certs = false;
+
 	for (var i = 0 ; i < num_systems ; i++) {
 		var element = document.getElementById('__SYSTEM' + i + ':Addr');
 
@@ -260,18 +286,31 @@
 		element.disabled = false;
 
 		var pwdElem = document.getElementById('__SYSTEM' + i + ':Passwd');
-		if (!element.value) {
-			if (pwdElem.value) {
+		if (!element.value || str_is_blank(element.value)) {
+			if (pwdElem && pwdElem.value && !str_is_blank(pwdElem.value)) {
 				set_form_err(element);
-				if (!allSameCB.checked) {
+				if (!allSameCB || !allSameCB.checked) {
 					errors.push('You entered a password, but no hostname for system ' + (i + 1));
 					clr_form_err(pwdElem);
 				} else
 					pwdElem.value = '';
 			}
-			clr_form_err(pwdElem);
+			if (pwdElem)
+				clr_form_err(pwdElem);
+			continue;
+		}
+
+		if (view_certs) {
+			if (pwdElem) {
+				pwdElem.value = ' ';
+				pwdElem.disabled = false;
+			}
+			added_storage.push(element.value);
+			clr_form_err(element);
 			continue;
-		} else if (!pwdElem || !pwdElem.value) {
+		}
+
+		if (!pwdElem || !pwdElem.value) {
 			errors.push('No password was given for \"' + element.value + '\"');
 			set_form_err(pwdElem);
 		} else if (str_is_blank(pwdElem.value)) {
--- conga/luci/homebase/luci_homebase.css	2007/01/10 22:53:56	1.28.2.4
+++ conga/luci/homebase/luci_homebase.css	2007/04/12 17:19:18	1.28.2.4.2.1
@@ -18,6 +18,14 @@
 	margin-left: +.3333em;
 }
 
+td.pad_right {
+	padding-right: +.6666em;
+}
+
+form.form_border {
+	border-bottom: 1px solid #8cacbb;
+}
+
 div.fence {
 	max-width: 700px;
 	padding: .5em;
@@ -281,7 +289,7 @@
 	display: none;
 }
 
-input.hbInputSys {
+input.hbInputSys, input.hostname {
 	padding: .2em ! important;
 	width: 200px;
 }
@@ -328,13 +336,11 @@
 strong.cluster {
 	text-align: top;
 	font-size: 9pt;
-	letter-spacing: +.5px;
 }
 
 *.reshdr {
 	text-align: top;
-	font-size: 9pt;
-	letter-spacing: +.5px;
+	font-size: 10pt;
 	font-weight: 600;
 	padding-bottom: +1em;
 }
@@ -343,7 +349,6 @@
 strong.node_name,
 strong.cluster_name {
 	font-size: 10pt;
-	letter-spacing: +.5px;
 }
 
 td.service_name,
@@ -407,6 +412,10 @@
 	text-decoration: none ! important;
 }
 
+*.cluster_help:hover {
+	cursor: help;
+}
+
 a.cluster_help:hover {
 	text-decoration: none ! important;
 	cursor: help;
@@ -490,7 +499,7 @@
 	max-width: 700px;
 }
 
-div.rescfg {
+*.rescfg {
 	background: #dee7ec;
 }
 
--- conga/luci/homebase/validate_cluster_add.js	2006/12/11 23:58:00	1.4.2.1
+++ conga/luci/homebase/validate_cluster_add.js	2007/04/12 17:19:18	1.4.2.1.2.1
@@ -10,12 +10,17 @@
 		set_form_err(form.clusterName);
 	} else {
 		clusterName = clusterName.value;
-		var invalid_chars = str_is_valid(clusterName, '/[0-9A-Za-z_. -]/g');
-		if (invalid_chars) {
-			errors.push('The cluster name you gave contains the following invalid characters: "' + invalid_chars + '".');
+		if (clusterName.length > 15) {
+			errors.push('A cluster\'s name must be less than 16 characters long.');
 			set_form_err(form.clusterName);
-		} else
-			clr_form_err(form.clusterName);
+		} else {
+			var invalid_chars = str_is_valid(clusterName, '/[0-9A-Za-z_. -]/g');
+			if (invalid_chars) {
+				errors.push('The cluster name you gave contains the following invalid characters: "' + invalid_chars + '".');
+				set_form_err(form.clusterName);
+			} else
+				clr_form_err(form.clusterName);
+		}
 	}
 
 	var added_storage = validate_systems(form, errors);
@@ -29,13 +34,17 @@
 	if (error_dialog(errors))
 		return (-1);
 
-	var confirm_str = '';
-	if (form.addnode)
-		confirm_str = 'Add node' + (added_storage.length > 1 ? 's' : '') + ' to the \"' + clusterName + '\" cluster?';
-	else
-		confirm_str = 'Add the cluster \"' + clusterName + '\" to the Luci management interface?';
-
-	if (confirm(confirm_str))
+	var view_certs = document.getElementById('view_certs');
+	if (!view_certs || !view_certs.checked) {
+		var confirm_str = '';
+		if (form.addnode)
+			confirm_str = 'Add ' + (added_storage.length > 1 ? 'these nodes' : 'this node') + ' to the \"' + clusterName + '\" cluster?';
+		else
+			confirm_str = 'Add the cluster \"' + clusterName + '\" to the Luci management interface?';
+
+		if (confirm(confirm_str))
+			form.submit();
+	} else
 		form.submit();
 
 	return (0);
--- conga/luci/plone-custom/footer	2006/11/16 19:34:53	1.2.2.1
+++ conga/luci/plone-custom/footer	2007/04/12 17:19:18	1.2.2.1.2.1
@@ -7,19 +7,17 @@
 		The 
 		<span>
 			<a href="http://www.sourceware.org/cluster/conga">
-				Conga Cluster and Storage Management System
-			</a>
+				Conga Cluster and Storage Management System</a>
 		</span>
 		is Copyright
 		<acronym title="Copyright" i18n:name="copyright" i18n:attributes="title title_copyright;">
 			©
 		</acronym>
-		2000-
-		<span
+		2000—<span
 			i18n:name="current_year" 
 			tal:define="now modules/DateTime/DateTime" 
 			tal:content="now/year" />
-		by <a href="http://www.redhat.com/">Red Hat, Inc</a>
+		<a href="http://www.redhat.com/">Red Hat, Inc.</a>
 	</span>
 </p>
 
/cvs/cluster/conga/luci/site/luci/Extensions/Apache.py,v  -->  standard output
revision 1.2.6.1
--- conga/luci/site/luci/Extensions/Apache.py
+++ -	2007-04-12 18:19:41.006649000 +0100
@@ -0,0 +1,15 @@
+import string
+from TagObject import TagObject
+from BaseResource import BaseResource
+
+import gettext
+_ = gettext.gettext
+
+TAG_NAME = "apache"
+RESOURCE_TYPE = _("Apache Server")
+
+class Apache(BaseResource):
+  def __init__(self):
+    BaseResource.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    self.resource_type = RESOURCE_TYPE
/cvs/cluster/conga/luci/site/luci/Extensions/FenceXVMd.py,v  -->  standard output
revision 1.1.6.1
--- conga/luci/site/luci/Extensions/FenceXVMd.py
+++ -	2007-04-12 18:19:41.086710000 +0100
@@ -0,0 +1,14 @@
+import string
+from TagObject import TagObject
+
+TAG_NAME = "fence_xvmd"
+
+class FenceXVMd(TagObject):
+  def __init__(self):
+    TagObject.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    #Have autostart set by default
+
+  def getProperties(self):
+    stringbuf = ""
+    return stringbuf
/cvs/cluster/conga/luci/site/luci/Extensions/LVM.py,v  -->  standard output
revision 1.1.6.1
--- conga/luci/site/luci/Extensions/LVM.py
+++ -	2007-04-12 18:19:41.173754000 +0100
@@ -0,0 +1,15 @@
+import string
+from TagObject import TagObject
+from BaseResource import BaseResource
+
+import gettext
+_ = gettext.gettext
+
+TAG_NAME = "lvm"
+RESOURCE_TYPE = _("LVM")
+
+class LVM(BaseResource):
+  def __init__(self):
+    BaseResource.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    self.resource_type = RESOURCE_TYPE
/cvs/cluster/conga/luci/site/luci/Extensions/MySQL.py,v  -->  standard output
revision 1.2.6.1
--- conga/luci/site/luci/Extensions/MySQL.py
+++ -	2007-04-12 18:19:41.255595000 +0100
@@ -0,0 +1,15 @@
+import string
+from TagObject import TagObject
+from BaseResource import BaseResource
+
+import gettext
+_ = gettext.gettext
+
+TAG_NAME = "mysql"
+RESOURCE_TYPE = _("MySQL Server")
+
+class MySQL(BaseResource):
+  def __init__(self):
+    BaseResource.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    self.resource_type = RESOURCE_TYPE
/cvs/cluster/conga/luci/site/luci/Extensions/OpenLDAP.py,v  -->  standard output
revision 1.2.6.1
--- conga/luci/site/luci/Extensions/OpenLDAP.py
+++ -	2007-04-12 18:19:41.336846000 +0100
@@ -0,0 +1,15 @@
+import string
+from TagObject import TagObject
+from BaseResource import BaseResource
+
+import gettext
+_ = gettext.gettext
+
+TAG_NAME = "openldap"
+RESOURCE_TYPE = _("Open LDAP Server")
+
+class OpenLDAP(BaseResource):
+  def __init__(self):
+    BaseResource.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    self.resource_type = RESOURCE_TYPE
/cvs/cluster/conga/luci/site/luci/Extensions/Postgres8.py,v  -->  standard output
revision 1.2.6.1
--- conga/luci/site/luci/Extensions/Postgres8.py
+++ -	2007-04-12 18:19:41.665881000 +0100
@@ -0,0 +1,15 @@
+import string
+from TagObject import TagObject
+from BaseResource import BaseResource
+
+import gettext
+_ = gettext.gettext
+
+TAG_NAME = "postgres-8"
+RESOURCE_TYPE = _("PostgreSQL 8 Server")
+
+class Postgres8(BaseResource):
+  def __init__(self):
+    BaseResource.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    self.resource_type = RESOURCE_TYPE
/cvs/cluster/conga/luci/site/luci/Extensions/Tomcat5.py,v  -->  standard output
revision 1.2.6.1
--- conga/luci/site/luci/Extensions/Tomcat5.py
+++ -	2007-04-12 18:19:41.797215000 +0100
@@ -0,0 +1,15 @@
+import string
+from TagObject import TagObject
+from BaseResource import BaseResource
+
+import gettext
+_ = gettext.gettext
+
+TAG_NAME = "tomcat-5"
+RESOURCE_TYPE = _("Tomcat 5 Server")
+
+class Tomcat5(BaseResource):
+  def __init__(self):
+    BaseResource.__init__(self)
+    self.TAG_NAME = TAG_NAME
+    self.resource_type = RESOURCE_TYPE
/cvs/cluster/conga/luci/site/luci/Extensions/Totem.py,v  -->  standard output
revision 1.1.6.1
--- conga/luci/site/luci/Extensions/Totem.py
+++ -	2007-04-12 18:19:41.948934000 +0100
@@ -0,0 +1,9 @@
+import string
+from TagObject import TagObject
+
+TAG_NAME = "totem"
+
+class Totem(TagObject):
+  def __init__(self):
+    TagObject.__init__(self)
+    self.TAG_NAME = TAG_NAME 
--- conga/luci/site/luci/Extensions/Cluster.py	2006/10/16 20:46:55	1.4
+++ conga/luci/site/luci/Extensions/Cluster.py	2007/04/12 17:19:18	1.4.4.1
@@ -8,7 +8,7 @@
 ACTUAL_CLUSTER_NAME_DESC = "The Actual Cluster Name should be used for storage configuration, such as making a clustered file system"
 CLUSTER_POPULATION = "Number of Members: %d"
 DLM_TYPE = "Locking Type: Distributed"
-GULM_TYPE = "Locking Type: GuLM"
+GULM_TYPE = "Locking Type: GULM"
 LOCKSERVER = "Lock Server:"
 CONFIG_VERSION = "Config Version"
 MCAST_MODE = "Cluster Manager using Multicast Mode. \n   Multicast Address: %s"
--- conga/luci/site/luci/Extensions/Clusterfs.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/Clusterfs.py	2007/04/12 17:19:18	1.1.4.1
@@ -6,7 +6,7 @@
 _ = gettext.gettext
 
 TAG_NAME = "clusterfs"
-RESOURCE_TYPE = _("GFS: ")
+RESOURCE_TYPE = _("GFS")
 
 class Clusterfs(BaseResource):
   def __init__(self):
--- conga/luci/site/luci/Extensions/FailoverDomain.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/FailoverDomain.py	2007/04/12 17:19:18	1.1.6.1
@@ -22,7 +22,7 @@
   def getProperties(self):
     stringbuf = ""
     restricted_status = ""
-    ordereded_status = ""
+    ordered_status = ""
     string_restricted = ""
     string_ordered = ""
     string_num_kin = ""
--- conga/luci/site/luci/Extensions/FenceHandler.py	2007/01/15 18:21:50	1.4.2.3
+++ conga/luci/site/luci/Extensions/FenceHandler.py	2007/04/12 17:19:18	1.4.2.3.2.1
@@ -1,1562 +1,1138 @@
-import os
-from ValidationError import ValidationError
-import MessageLibrary
-import ModelBuilder
-#from FenceDevice import FenceDevice
+import re
+from Device import Device
 from conga_constants import FD_VAL_SUCCESS, FD_VAL_FAIL
 
-INSTALLDIR="/usr/share/system-config-cluster"
+FD_NEW_SUCCESS = 'New %s successfully added to cluster'
+FD_UPDATE_SUCCESS = 'Fence device %s successfully updated'
+FD_NEW_FAIL = 'No agent type %s in shared device list'
+
+FI_NEW_FAIL = 'No instance type %s in fence instance list'
+
+FD_PROVIDE_AGENT = 'A valid agent type must be provided for each Fence Device'
+FD_PROVIDE_NAME = 'A unique name must be provided for each Fence Device'
+FD_PROVIDE_PATH = 'An xCAT path must be provided for each xCAT Fence Device'
+FD_PROVIDE_SERVER = 'A server address must be provided for this Fence Device'
+FD_PROVIDE_CSERVER = 'A cserver address must be provided for this Egenera Fence Device'
+FD_PROVIDE_IP = 'An IP address must be provided for this Fence Device'
+FD_PROVIDE_HOSTNAME = 'A host name must be provided for this Fence Device'
+FD_PROVIDE_LOGIN = 'A login name must be provided for this Fence Device'
+FD_PROVIDE_PASSWD = 'A password must be provided for this Fence Device'
+FD_PROVIDE_DEVICE = 'A device must be provided for this Fence Device'
+FD_PROVIDE_PORT = 'A port must be provided for this Fence Device'
+FD_PROVIDE_IPMILAN_AUTH = 'Authentication type must be "none" (or blank), "md5", or "password"'
+
+FI_PROVIDE_PARENT = 'A Fence Device name must be provided for this Fence'
+FI_PROVIDE_SWITCH = 'A switch address must be provided for this Fence'
+FI_PROVIDE_PORT = 'A port value must be provided for this Fence'
+FI_PROVIDE_BLADE = 'A Blade must be specified for this Fence'
+FI_PROVIDE_DOMAIN = 'A Domain must be specified for this Fence'
+FI_PROVIDE_IPADDRESS = 'An IP address must be provided for this Fence'
+FI_PROVIDE_ELPAN = 'A LPAN value must be provided for this Egenera Fence'
+FI_PROVIDE_EPSERVER = 'A PServer value must be provided for this Egenera Fence'
+FI_PROVIDE_NODENAME = 'A Node Name value must be provided for this SCSI Fence'
+
+ILLEGAL_CHARS_REPLACED = 'Illegal characters were replaced by underscores. Feel free to set a new value.'
+
+# For every new fence added, there will be a fence instance and fence device
+# form needed. There will also be a populate and a validate method for each
+# form. The methods will need to be added to this file, and the four hash
+# tables (self.fi_populate, self.fi_validate, self.fd_populate,
+# self.fd_validate) must be updated. The methods will use fields in the
+# forms to set_text and to check.
+
+FENCE_OPTS = {
+	'fence_apc':			'APC Power Device',
+	'fence_wti':			'WTI Power Device',
+	'fence_brocade':		'Brocade Switch',
+	'fence_vixel':			'Vixel SAN Switch',
+	'fence_gnbd':			'Global Network Block Device',
+	'fence_sanbox2':		'QLogic SANBox2',
+	'fence_bladecenter':	'IBM Blade Center',
+	'fence_mcdata':			'McDATA SAN Switch',
+	'fence_egenera':		'Egenera SAN Controller',
+	'fence_bullpap':		'Bull PAP',
+	'fence_xvm':			'Virtual Machine Fencing',
+	'fence_scsi':			'SCSI Reservation',
+	'fence_ilo':			'HP ILO Device',
+	'fence_ipmilan':		'IPMI Lan',
+	'fence_drac':			'Dell DRAC',
+	'fence_rsa':			'IBM RSA II Device',
+	'fence_rps10':			'RPS10 Serial Switch',
+	'fence_manual':			'Manual Fencing'
+}
+
+FENCE_SHARED = {
+	'fence_apc':			True,
+	'fence_wti':			True,
+	'fence_brocade':		True,
+	'fence_vixel':			True,
+	'fence_gnbd':			True,
+	'fence_sanbox2':		True,
+	'fence_bladecenter':	True,
+	'fence_mcdata':			True,
+	'fence_egenera':		True,
+	'fence_bullpap':		True,
+	'fence_xvm':			True,
+	'fence_scsi':			True,
+	'fence_ilo':			False,
+	'fence_ipmilan':		False,
+	'fence_drac':			False,
+	'fence_rsa':			False,
+	'fence_rps10':			False,
+	'fence_manual':			False
+}
+
+PRETTY_NAME_ATTRS = {
+	'port':					'Port',
+	'blade':				'Blade',
+	'switch':				'Switch',
+	'ipaddr':				'IP Address',
+	'ipaddress':			'IP Address',
+	'nodename':				'Node Name',
+	'lpan':					'LPAN',
+	'lanplus':				'Lanplus',
+	'pserver':				'PServer',
+	'login':				'Login',
+	'passwd':				'Password',
+	'name':					'Name',
+	'server':				'Server',
+	'domain':				'Domain',
+	'hostname':				'Hostname',
+	'path':					'Path',
+	'cserver':				'CServer'
+}
+
+FENCE_FI_ATTRS = {
+	'fence_apc':			['port', 'switch'],
+	'fence_wti':			['port'],
+	'fence_brocade':		['port'],
+	'fence_vixel':			['port'],
+	'fence_gnbd':			['ipaddress'],
+	'fence_sanbox2':		['port'],
+	'fence_bladecenter':	['blade'],
+	'fence_mcdata':			['port'],
+	'fence_egenera':		['lpan', 'pserver'],
+	'fence_bullpap':		['domain'],
+	'fence_xvm':			['domain'],
+	'fence_scsi':			['nodename'],
+	'fence_ilo':			[],
+	'fence_ipmilan':		[],
+	'fence_drac':			[],
+	'fence_rsa':			[],
+	'fence_rps10':			[],
+	'fence_manual':			[]
+}
+
+FENCE_FD_ATTRS = {
+	'fence_apc': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_wti': ['name', 'ipaddr', 'passwd'],
+	'fence_brocade': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_vixel': ['name', 'ipaddr', 'passwd'],
+	'fence_gnbd': ['name', 'servers'],
+	'fence_sanbox2': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_bladecenter': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_mcdata': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_egenera': ['name', 'cserver'],
+	'fence_bullpap': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_xvm': ['name'],
+	'fence_scsi': ['name'],
+	'fence_ilo': ['name', 'hostname', 'login', 'passwd'],
+	'fence_ipmilan': ['name', 'ipaddr', 'login', 'passwd', 'lanplus', 'auth'],
+	'fence_drac': ['name', 'ipaddr', 'login', 'passwd'],
+	'fence_rsa': ['name', 'hostname', 'login', 'passwd'],
+	'fence_rps10': ['name', 'device', 'port'],
+	'fence_manual': ['name']
+}
+
+ILLEGAL_CHARS = re.compile(':| ')
+
+def makeNCName(name):
+	### name must conform to relaxNG ID type ##
+	return ILLEGAL_CHARS.sub('_', name)
+
+def check_unique_fd_name(model, name):
+	fds = model.getFenceDevices()
+	for fd in fds:
+		if fd.getName() == name:
+			return False
+	return True
+
+def validateNewFenceDevice(form, model):
+	from FenceDevice import FenceDevice
+	fencedev = FenceDevice()
+
+	try:
+		ret = validate_fencedevice(form, model, fencedev)
+		if len(ret) < 1:
+			fencedevptr = model.getFenceDevicePtr()
+			fencedevptr.addChild(fencedev)
+			model.setModified(True)
+			return (FD_VAL_SUCCESS, fencedev.getAttribute('name'))
+	except Exception, e:
+		ret = [ FD_PROVIDE_AGENT ]
+
+	return (FD_VAL_FAIL, ret)
+
+def validateFenceDevice(form, model):
+	from FenceDevice import FenceDevice
+	try:
+		old_fence_name = form['orig_name'].strip()
+		if not old_fence_name:
+			raise Exception, 'blank'
+	except Exception, e:
+		return (FD_VAL_FAIL, [ FD_PROVIDE_NAME ])
+
+	fencedev = None
+	try:
+		fencedevs = model.getFenceDevices()
+		for fd in fencedevs:
+			if fd.getName().strip() == old_fence_name:
+				fencedev = fd
+				break
+		if fencedev is None:
+			raise Exception, 'fencedev is None'
+	except Exception, e:
+		return (FD_VAL_FAIL, [ FD_PROVIDE_NAME ])
+
+	try:
+		ret = validate_fencedevice(form, model, fencedev, fence_edit=True)
+		if len(ret) < 1:
+			model.setModified(True)
+			return (FD_VAL_SUCCESS, fencedev.getAttribute('name'))
+	except Exception, e:
+		ret = [ FD_PROVIDE_NAME ]
+
+	return (FD_VAL_FAIL, ret)
+
+def val_apc_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_wti_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	return errors
+
+def val_brocade_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_vixel_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	return errors
+
+def val_mcdata_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_gnbd_fd(form, fencedev):
+	errors = list()
+
+	try:
+		server = form['servers'].strip()
+		if not server:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_SERVER)
+
+	fencedev.addAttribute('servers', server)
+	return errors
+
+def val_egenera_fd(form, fencedev):
+	errors = list()
+
+	try:
+		cserver = form['cserver'].strip()
+		if not cserver:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_CSERVER)
+
+	fencedev.addAttribute('cserver', cserver)
+	return errors
+
+def val_sanbox2_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_bladecenter_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_bullpap_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_noop_fd(dummy, _dummy):
+	return []
+
+# non-shared devices
+
+def val_rsa_fd(form, fencedev):
+	errors = list()
+
+	try:
+		hostname = form['hostname'].strip()
+		if not hostname:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_HOSTNAME)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('hostname', hostname)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_drac_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_rps10_fd(form, fencedev):
+	errors = list()
+
+	try:
+		device = form['device'].strip()
+		if not device:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_DEVICE)
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_PORT)
+
+	fencedev.addAttribute('device', device)
+	fencedev.addAttribute('port', port)
+	return errors
+
+def val_ipmilan_fd(form, fencedev):
+	errors = list()
+
+	try:
+		ip = form['ipaddr'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_IP)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	try:
+		auth_type = form['auth_type'].strip().lower()
+		if not auth_type or auth_type == 'none':
+			fencedev.removeAttribute('auth_type')
+		elif auth_type == 'password' or auth_type == 'md5':
+			fencedev.setAttribute('auth_type', auth_type)
+		else:
+			errors.append(FD_PROVIDE_IPMILAN_AUTH)
+	except KeyError, e:
+		fencedev.removeAttribute('auth_type')
+	except Exception, e:
+		errors.append(FD_PROVIDE_IPMILAN_AUTH)
+
+	try:
+		lanplus = form['lanplus'].strip().lower()
+		if not lanplus or lanplus == '0' or lanplus == 'false' or lanplus == 'off':
+			fencedev.removeAttribute('lanplus')
+		else:
+			fencedev.setAttribute('lanplus', '1')
+	except Exception, e:
+		fencedev.removeAttribute('lanplus')
+
+	fencedev.addAttribute('ipaddr', ip)
+	fencedev.addAttribute('login', log)
+	return errors
+
+def val_ilo_fd(form, fencedev):
+	errors = list()
+
+	try:
+		hostname = form['hostname'].strip()
+		if not hostname:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_HOSTNAME)
+
+	try:
+		log = form['login'].strip()
+		if not log:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FD_PROVIDE_LOGIN)
+
+	has_passwd = False
+	try:
+		pwd = form['passwd'].strip()
+		if not pwd:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd', pwd)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd')
+		except:
+			pass
+
+	try:
+		pwd_script = form['passwd_script'].strip()
+		if not pwd_script:
+			raise Exception, 'blank'
+		fencedev.addAttribute('passwd_script', pwd_script)
+		has_passwd = True
+	except Exception, e:
+		try:
+			fencedev.removeAttribute('passwd_script')
+		except:
+			pass
+
+	if not has_passwd:
+		errors.append(FD_PROVIDE_PASSWD)
+
+	fencedev.addAttribute('hostname', hostname)
+	fencedev.addAttribute('login', log)
+	return errors
+
+FD_VALIDATE = {
+	'fence_apc':			val_apc_fd,
+	'fence_wti':			val_wti_fd,
+	'fence_brocade':		val_brocade_fd,
+	'fence_vixel':			val_vixel_fd,
+	'fence_gnbd':			val_gnbd_fd,
+	'fence_sanbox2':		val_sanbox2_fd,
+	'fence_bladecenter':	val_bladecenter_fd,
+	'fence_mcdata':			val_mcdata_fd,
+	'fence_egenera':		val_egenera_fd,
+	'fence_bullpap':		val_bullpap_fd,
+	'fence_xvm':			val_noop_fd,
+	'fence_scsi':			val_noop_fd,
+	'fence_ilo':			val_ilo_fd,
+	'fence_ipmilan':		val_ipmilan_fd,
+	'fence_drac':			val_drac_fd,
+	'fence_rsa':			val_rsa_fd,
+	'fence_rps10':			val_rps10_fd,
+	'fence_manual':			val_noop_fd
+}
+
+def validate_fencedevice(form, model, fencedev, fence_edit=False):
+	try:
+		fence_name = form['name'].strip()
+		if not fence_name:
+			raise Exception, 'blank'
+		fence_name = makeNCName(fence_name)
+	except Exception, e:
+		return [ FD_PROVIDE_NAME ]
+
+	name_change = False
+	if fence_edit is True:
+		try:
+			old_fence_name = form['orig_name'].strip()
+			if not old_fence_name:
+				raise Exception, 'blank'
+		except Exception, e:
+			return [ FD_PROVIDE_NAME ]
+		if old_fence_name != fence_name:
+			if check_unique_fd_name(model, fence_name) is False:
+				return [ FD_PROVIDE_NAME ]
+			name_change = True
+	else:
+		if check_unique_fd_name(model, fence_name) is False:
+			return [ FD_PROVIDE_NAME ]
+
+	try:
+		fence_agent = form['fence_type'].strip()
+		if not fence_agent:
+			raise Exception, 'blank agent'
+	except Exception, e:
+		return [ FD_PROVIDE_AGENT ]
+
+	fencedev.addAttribute('name', fence_name)
+	fencedev.addAttribute('agent', fence_agent)
+
+	try:
+		ret = FD_VALIDATE[fence_agent](form, fencedev)
+		if len(ret) < 1 and name_change is True:
+			try:
+				model.rectifyNewFencedevicenameWithFences(old_fence_name, fence_name)
+			except:
+				return [ FD_NEW_FAIL % fence_agent ]
+		return ret
+	except:
+		return [ FD_NEW_FAIL % fence_agent ]
+
+# Validation Methods for Fence Instances
+
+def val_apc_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_PORT)
+
+	try:
+		switch = form['switch'].strip()
+		if not switch:
+			raise KeyError, 'blank'
+		fenceinst.addAttribute('switch', switch)
+	except KeyError, e:
+		# switch is optional
+		fenceinst.removeAttribute('switch')
+	except Exception, e:
+		errors.append(FI_PROVIDE_SWITCH)
+
+	fenceinst.addAttribute('port', port)
+	return errors
+
+def val_wti_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_PORT)
+
+	fenceinst.addAttribute('port', port)
+	return errors
+
+def val_brocade_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_PORT)
+
+	fenceinst.addAttribute('port', port)
+	return errors
+
+def val_vixel_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_PORT)
+
+	fenceinst.addAttribute('port', port)
+	return errors
+
+def val_gnbd_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		ip = form['ipaddress'].strip()
+		if not ip:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_IPADDRESS)
+
+	fenceinst.addAttribute('ipaddress', ip)
+	return errors
+
+def val_sanbox2_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_PORT)
+
+	fenceinst.addAttribute('port', port)
+	return errors
+
+def val_bladecenter_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		blade = form['blade'].strip()
+		if not blade:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_BLADE)
+
+	fenceinst.addAttribute('blade', blade)
+	return errors
+
+def val_mcdata_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		port = form['port'].strip()
+		if not port:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_PORT)
+
+	fenceinst.addAttribute('port', port)
+	return errors
+
+def val_egenera_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		lpan = form['lpan'].strip()
+		if not lpan:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_ELPAN)
+
+	try:
+		pserver = form['pserver'].strip()
+		if not pserver:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_ELPAN)
+
+	fenceinst.addAttribute('lpan', lpan)
+	fenceinst.addAttribute('pserver', pserver)
+	return errors
+
+def val_bullpap_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		domain = form['domain'].strip()
+		if not domain:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_DOMAIN)
+
+	fenceinst.addAttribute('domain', domain)
+	return errors
+
+def val_xvm_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		domain = form['domain'].strip()
+		if not domain:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_DOMAIN)
+
+	fenceinst.addAttribute('domain', domain)
+	return errors
+
+def val_scsi_fi(form, fenceinst):
+	errors = list()
+
+	try:
+		nodename = form['nodename'].strip()
+		if not nodename:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append(FI_PROVIDE_NODENAME)
+
+	fenceinst.addAttribute('nodename', nodename)
+	return errors
+
+def val_noop_fi(dummy, _dummy):
+	return []
+
+FI_VALIDATE = {
+	'fence_apc':			val_apc_fi,
+	'fence_wti':			val_wti_fi,
+	'fence_brocade':		val_brocade_fi,
+	'fence_vixel':			val_vixel_fi,
+	'fence_gnbd':			val_gnbd_fi,
+	'fence_sanbox2':		val_sanbox2_fi,
+	'fence_bladecenter':	val_bladecenter_fi,
+	'fence_mcdata':			val_mcdata_fi,
+	'fence_egenera':		val_egenera_fi,
+	'fence_bullpap':		val_bullpap_fi,
+	'fence_xvm':			val_xvm_fi,
+	'fence_scsi':			val_scsi_fi,
+	'fence_ilo':			val_noop_fi,
+	'fence_ipmilan':		val_noop_fi,
+	'fence_drac':			val_noop_fi,
+	'fence_rsa':			val_noop_fi,
+	'fence_rps10':			val_noop_fi,
+	'fence_manual':			val_noop_fi
+}
+
+def validate_fenceinstance(form, parent_name, fence_agent):
+	try:
+		if not parent_name.strip():
+			return (FD_VAL_FAIL, [ FI_PROVIDE_PARENT ])
+	except:
+		return (FD_VAL_FAIL, [ FI_PROVIDE_PARENT ])
+
+	fenceinst = Device()
+	fenceinst.addAttribute('name', parent_name)
+
+	try:
+		ret = FI_VALIDATE[fence_agent](form, fenceinst)
+		if len(ret) > 0:
+			return (FD_VAL_FAIL, ret)
+	except Exception, e:
+		return (FD_VAL_FAIL, [ FI_NEW_FAIL % fence_agent ])
 
-FD_NEW_SUCCESS = "New %s successfully added to cluster"
-FD_UPDATE_SUCCESS = "Fence device %s successfully updated"
-FD_NEW_FAIL = "No agent type %s in shared device list"
-FD_PROVIDE_NAME = "A unique name must be provided for each Fence Device"
-
-FD_PROVIDE_PATH = "An xCAT path must be provided for each xCAT Fence Device"
-FD_PROVIDE_SERVER = "A server address must be provided for this Fence Device"
-FD_PROVIDE_CSERVER = "A cserver address must be provided for this Egenera Fence Device"
-FD_PROVIDE_IP = "An IP address must be provided for this Fence Device"
-FD_PROVIDE_HOSTNAME = "A host name must be provided for this Fence Device"
-FD_PROVIDE_LOGIN = "A login name must be provided for this Fence Device"
-FD_PROVIDE_PASSWD = "A password must be provided for this Fence Device"
-FI_PROVIDE_XCATNODENAME = "An xCAT Nodename must be provided for this Fence"
-FI_PROVIDE_SWITCH = "A switch address must be provided for this Fence"
-FI_PROVIDE_PORT = "A port value must be provided for this Fence"
-FI_PROVIDE_BLADE = "A Blade must be specified for this Fence"
-FI_PROVIDE_DOMAIN = "A Domain must be specified for this Fence"
-FI_PROVIDE_IPADDRESS = "An IP address must be provided for this Fence"
-FI_PROVIDE_ELPAN = "A LPAN value must be provided for this Egenera Fence"
-FI_PROVIDE_EPSERVER = "A PServer value must be provided for this Egenera Fence"
-
-ILLEGAL_CHARS_REPLACED = "Illegal characters were replaced by underscores. Feel free to set a new value."
-ILLEGAL_CHARS = [':', ' ']
-
-#ADDING A NEW FENCE: fence instance form should be named the same as its agent
-#in gladefile. Then add agent name to this list.
-#
-#In Glade File, fence device form should be added to 'fence_device_container'
-#and fence instance form to fence_instance_container
-#
-#For every new fence added, there will be a fence instance and fence device
-#form needed. There will also be a populate and a validate method for each
-#form. The methods will need to be added to this file, and the four hash
-#tables (self.fi_populate, self.fi_validate, self.fd_populate, 
-#self.fd_validate) must be updated. The methods will use fields in the
-#forms to set_text and to check.
-
-FENCE_OPTS = {"fence_apc":"APC Power Device",
-              "fence_wti":"WTI Power Device",
-              "fence_brocade":"Brocade Switch",
-              "fence_vixel":"Vixel SAN Switch",
-              "fence_gnbd":"Global Network Block Device",
-              "fence_ilo":"HP ILO Device",
-              "fence_rsa":"IBM RSA II Device",
-              "fence_sanbox2":"QLogic SANBox2",
-              "fence_bladecenter":"IBM Blade Center",
-              "fence_mcdata":"McDATA SAN Switch",
-              "fence_egenera":"Egenera SAN Controller",
-              "fence_bullpap":"Bull PAP",
-              "fence_drac":"DRAC",
-              "fence_xvm":"Virtual Machine Fencing",
-              "fence_scsi":"SCSI Reservation",
-              "fence_ipmilan":"IPMI Lan",
-              "fence_manual":"Manual Fencing" }
-
-FENCE_SHARED = {"fence_apc":True,
-              "fence_wti":True,
-              "fence_brocade":True,
-              "fence_vixel":True,
-              "fence_gnbd":True,
-              "fence_ilo":False,
-              "fence_rsa":False,
-              "fence_sanbox2":True,
-              "fence_bladecenter":True,
-              "fence_mcdata":True,
-              "fence_egenera":True,
-              "fence_bullpap":True,
-              "fence_drac":False,
-              "fence_xvm":True,
-              "fence_scsi":True,
-              "fence_ipmilan":False,
-              "fence_manual":False }
-
-FENCE_FD_ATTRS = {"fence_apc":["name","ipaddr","login","passwd"],
-              "fence_wti":["name","ipaddr","passwd"],
-              "fence_brocade":["name","ipaddr","login","passwd"],
-              "fence_vixel":["name","ipaddr","passwd"],
-              "fence_gnbd":["name","server"],
-              "fence_ilo":["name","hostname","login","passwd"],
-              "fence_sanbox2":["name","ipaddr","login","passwd"],
-              "fence_bladecenter":["name","ipaddr","login","passwd"],
-              "fence_mcdata":["name","ipaddr","login","passwd"],
-              "fence_egenera":["name","cserver"],
-              "fence_ipmilan":["name","ipaddr","login","passwd"],
-              "fence_bullpap":["name","ipaddr","login","passwd"],
-              "fence_manual":["name"] }
-
-FENCE_FI_ATTRS = {"fence_apc":["port","switch"],
-              "fence_wti":["port"],
-              "fence_brocade":["port"],
-              "fence_vixel":["port"],
-              "fence_gnbd":["ipaddress"],
-              "fence_ilo":[],
-              "fence_sanbox2":["port"],
-              "fence_bladecenter":["blade"],
-              "fence_mcdata":["port"],
-              "fence_egenera":["lpan","pserver"],
-              "fence_ipmilan":[],
-              "fence_bullpap":["domain"],
-              "fence_manual":[] }
-
-PRETTY_NAME_ATTRS = {"port":"Port",
-                     "blade":"Blade", 
-                     "switch":"Switch",
-                     "ipaddr":"IP Address",
-                     "nodename":"Nodename",
-                     "lpan":"LPAN",
-                     "pserver":"PServer",
-                     "login":"Login",
-                     "passwd":"Password",
-                     "name":"Name",
-                     "server":"Server",
-                     "domain":"Domain",
-                     "hostname":"Hostname",
-                     "path":"Path",
-                     "cserver":"CServer" }  
-
-class FenceHandler:
-  def __init__(self, fi_proxy_widget, fd_proxy_widget, model_builder):
-    self.fi_proxy_widget = fi_proxy_widget
-    self.fd_proxy_widget = fd_proxy_widget
-    self.model_builder = model_builder
-    gladepath = "fence.glade"
-    if not os.path.exists(gladepath):
-      gladepath = "%s/%s" % (INSTALLDIR,gladepath)
-                                                                                
-    #gtk.glade.bindtextdomain(PROGNAME)
-    self.fence_xml = gtk.glade.XML (gladepath, domain="NULL")
-
-    #Generate hash table for agent --> fence instance form
-    self.fi_hash = { }
-    fence_opt_keys = FENCE_OPTS.keys()
-    for fence in fence_opt_keys:
-      self.fi_hash[fence] = self.fence_xml.get_widget(fence)
-
-    #Generate hash table for agent --> fence device form
-    self.fd_hash = { }
-    fence_opt_keys = FENCE_OPTS.keys()
-    for fence in fence_opt_keys:
-      self.fd_hash[fence] = self.fence_xml.get_widget(fence + "_d")
-
-    self.pretty_agentname_hash = FENCE_OPTS
-
-    self.fi_container = self.fence_xml.get_widget('fence_instance_container')
-    children = self.fi_container.get_children()
-    for child in children:
-      child.reparent(self.fi_proxy_widget)
-    
-    self.fi_container2 = self.fence_xml.get_widget('fence_instance_container2')
-    children2 = self.fi_container2.get_children()
-    for child in children2:
-      child.reparent(self.fi_proxy_widget)
-    
-    self.fd_container = self.fence_xml.get_widget('fence_device_container')
-    children = self.fd_container.get_children()
-    for child in children:
-      child.reparent(self.fd_proxy_widget)
-    
-    self.fd_container2 = self.fence_xml.get_widget('fence_device_container2')
-    children2 = self.fd_container2.get_children()
-    for child in children2:
-      child.reparent(self.fd_proxy_widget)
-    
-    #For testing...
-    #self.fence_xml.get_widget('fi_wti').show()
-
-    self.fi_populate = {"fence_apc":self.pop_apc,
-              "fence_wti":self.pop_wti,
-              "fence_brocade":self.pop_brocade,
-              "fence_vixel":self.pop_vixel,
-              "fence_gnbd":self.pop_gnbd,
-              "fence_ilo":self.pop_ilo,
-              "fence_sanbox2":self.pop_sanbox2,
-              "fence_bladecenter":self.pop_bladecenter,
-              "fence_mcdata":self.pop_mcdata,
-              "fence_egenera":self.pop_egenera,
-              "fence_ipmilan":self.pop_ipmilan,
-              "fence_bullpap":self.pop_bullpap,
-              "fence_manual":self.pop_manual }
-
-    self.fd_populate = {"fence_apc":self.pop_apc_fd,
-              "fence_wti":self.pop_wti_fd,
-              "fence_brocade":self.pop_brocade_fd,
-              "fence_vixel":self.pop_vixel_fd,
-              "fence_gnbd":self.pop_gnbd_fd,
-              "fence_ilo":self.pop_ilo_fd,
-              "fence_sanbox2":self.pop_sanbox2_fd,
-              "fence_bladecenter":self.pop_bladecenter_fd,
-              "fence_mcdata":self.pop_mcdata_fd,
-              "fence_egenera":self.pop_egenera_fd,
-              "fence_ipmilan":self.pop_ipmilan_fd,
-              "fence_bullpap":self.pop_bullpap_fd,
-              "fence_manual":self.pop_manual_fd }
-
-    self.fi_validate = {"fence_apc":self.val_apc,
-              "fence_wti":self.val_wti,
-              "fence_brocade":self.val_brocade,
-              "fence_vixel":self.val_vixel,
-              "fence_gnbd":self.val_gnbd,
-              "fence_ilo":self.val_ilo,
-              "fence_sanbox2":self.val_sanbox2,
-              "fence_bladecenter":self.val_bladecenter,
-              "fence_mcdata":self.val_mcdata,
-              "fence_egenera":self.val_egenera,
-              "fence_ipmilan":self.val_ipmilan,
-              "fence_bullpap":self.val_bullpap,
-              "fence_manual":self.val_manual }
-
-    self.fd_validate = {"fence_apc":self.val_apc_fd,
-              "fence_wti":self.val_wti_fd,
-              "fence_brocade":self.val_brocade_fd,
-              "fence_vixel":self.val_vixel_fd,
-              "fence_gnbd":self.val_gnbd_fd,
-              "fence_ilo":self.val_ilo_fd,
-              "fence_sanbox2":self.val_sanbox2_fd,
-              "fence_bladecenter":self.val_bladecenter_fd,
-              "fence_mcdata":self.val_mcdata_fd,
-              "fence_egenera":self.val_egenera_fd,
-              "fence_ipmilan":self.val_ipmilan_fd,
-              "fence_bullpap":self.val_bullpap_fd,
-              "fence_manual":self.val_manual_fd }
-
-    self.process_widgets()
-
-  def get_fence_instance_hash(self):
-    return self.fi_hash
-
-  def get_fence_device_hash(self):
-    return self.fd_hash
-
-  def populate_fi_form(self, agent_type, *attrs):
-    apply(self.fi_populate[agent_type], attrs)
-
-  def populate_fd_form(self, agent_type, *attrs):
-    apply(self.fd_populate[agent_type], attrs)
-
-  def pop_apc(self, attrs):
-    self.apc_port.set_text(attrs["port"]) 
-    self.apc_switch.set_text(attrs["switch"]) 
- 
-  def pop_wti(self, attrs):
-    self.wti_port.set_text(attrs["port"])
- 
-  def pop_brocade(self, attrs):
-    self.brocade_port.set_text(attrs["port"])
- 
-  def pop_ilo(self, attrs):
-    pass
- 
-  def pop_vixel(self, attrs):
-    self.vixel_port.set_text(attrs["port"])
- 
-  def pop_mcdata(self, attrs):
-    self.mcdata_port.set_text(attrs["port"])
- 
-  def pop_manual(self, attrs):
-    pass
- 
-  def pop_gnbd(self, attrs):
-    self.gnbd_ip.set_text(attrs["ipaddress"])
- 
-  def pop_egenera(self, attrs):
-    self.egenera_lpan.set_text(attrs["lpan"])
-    self.egenera_pserver.set_text(attrs["pserver"])
- 
-  def pop_sanbox2(self, attrs):
-    self.sanbox2_port.set_text(attrs["port"])
-
-  def pop_bladecenter(self, attrs):
-    self.bladecenter_blade.set_text(attrs["blade"])
-
-  def pop_bullpap(self, attrs):
-    self.bullpap_domain.set_text(attrs["domain"])
-
-  def pop_ipmilan(self, attrs):
-    pass
- 
-  def clear_fi_forms(self):
-    self.apc_port.set_text("") 
-    self.apc_switch.set_text("") 
-    self.wti_port.set_text("") 
-    self.brocade_port.set_text("")
-    self.vixel_port.set_text("")
-    self.gnbd_ip.set_text("") 
-    self.sanbox2_port.set_text("")
-    self.bladecenter_blade.set_text("")
-    self.mcdata_port.set_text("")
-    self.egenera_lpan.set_text("")
-    self.egenera_pserver.set_text("")
-    self.bullpap_domain.set_text("")
-
-
-  def clear_fd_forms(self):
-    self.apc_fd_name.set_text("") 
-    self.apc_fd_ip.set_text("")
-    self.apc_fd_login.set_text("")
-    self.apc_fd_passwd.set_text("")
-    self.wti_fd_ip.set_text("")
-    self.wti_fd_name.set_text("")
-    self.wti_fd_passwd.set_text("")
-    self.brocade_fd_name.set_text("")
-    self.brocade_fd_ip.set_text("")
-    self.brocade_fd_login.set_text("")
-    self.brocade_fd_passwd.set_text("")
-    self.ilo_fd_name.set_text("")
-    self.ilo_fd_login.set_text("")
-    self.ilo_fd_passwd.set_text("")
-    self.ilo_fd_hostname.set_text("")
-    self.vixel_fd_name.set_text("")
-    self.vixel_fd_ip.set_text("")
-    self.vixel_fd_passwd.set_text("")
-    self.manual_fd_name.set_text("")
-    self.mcdata_fd_name.set_text("")
-    self.mcdata_fd_ip.set_text("")
-    self.mcdata_fd_login.set_text("")
-    self.mcdata_fd_passwd.set_text("")
-    self.gnbd_fd_name.set_text("")
-    self.gnbd_fd_server.set_text("")
-    self.egenera_fd_name.set_text("")
-    self.egenera_fd_cserver.set_text("")
-    self.sanbox2_fd_name.set_text("")
-    self.sanbox2_fd_ip.set_text("")
-    self.sanbox2_fd_login.set_text("")
-    self.sanbox2_fd_passwd.set_text("")
-    self.bladecenter_fd_name.set_text("")
-    self.bladecenter_fd_ip.set_text("")
-    self.bladecenter_fd_login.set_text("")
-    self.bladecenter_fd_passwd.set_text("")
-    self.ipmilan_fd_name.set_text("")
-    self.ipmilan_fd_login.set_text("")
-    self.ipmilan_fd_passwd.set_text("")
-    self.ipmilan_fd_ip.set_text("")
-    self.bullpap_fd_name.set_text("")
-    self.bullpap_fd_login.set_text("")
-    self.bullpap_fd_passwd.set_text("")
-    self.bullpap_fd_ip.set_text("")
-
-  #Populate form methods for Fence Devices
-  def pop_apc_fd(self, attrs):
-    self.apc_fd_name.set_text(attrs["name"]) 
-    self.apc_fd_ip.set_text(attrs["ipaddr"])
-    self.apc_fd_login.set_text(attrs["login"])
-    self.apc_fd_passwd.set_text(attrs["passwd"])
-
- 
-  def pop_wti_fd(self, attrs):
-    self.wti_fd_ip.set_text(attrs["ipaddr"])
-    self.wti_fd_name.set_text(attrs["name"])
-    self.wti_fd_passwd.set_text(attrs["passwd"])
- 
-  def pop_brocade_fd(self, attrs):
-    self.brocade_fd_name.set_text(attrs["name"])
-    self.brocade_fd_ip.set_text(attrs["ipaddr"])
-    self.brocade_fd_login.set_text(attrs["login"])
-    self.brocade_fd_passwd.set_text(attrs["passwd"])
-
- 
-  def pop_ilo_fd(self, attrs):
-    self.ilo_fd_name.set_text(attrs["name"])
-    self.ilo_fd_login.set_text(attrs["login"])
-    self.ilo_fd_passwd.set_text(attrs["passwd"])
-    self.ilo_fd_hostname.set_text(attrs["hostname"])
-
- 
-  def pop_vixel_fd(self, attrs):
-    self.vixel_fd_name.set_text(attrs["name"])
-    self.vixel_fd_ip.set_text(attrs["ipaddr"])
-    self.vixel_fd_passwd.set_text(attrs["passwd"])
-
- 
-  def pop_mcdata_fd(self, attrs):
-    self.mcdata_fd_name.set_text(attrs["name"])
-    self.mcdata_fd_ip.set_text(attrs["ipaddr"])
-    self.mcdata_fd_login.set_text(attrs["login"])
-    self.mcdata_fd_passwd.set_text(attrs["passwd"])
-
- 
-  def pop_manual_fd(self, attrs):
-    self.manual_fd_name.set_text(attrs["name"])
- 
-  def pop_gnbd_fd(self, attrs):
-    self.gnbd_fd_name.set_text(attrs["name"])
-    self.gnbd_fd_server.set_text(attrs["server"])
-
- 
-  def pop_egenera_fd(self, attrs):
-    self.egenera_fd_name.set_text(attrs["name"])
-    self.egenera_fd_cserver.set_text(attrs["cserver"])
-
- 
-  def pop_sanbox2_fd(self, attrs):
-    self.sanbox2_fd_name.set_text(attrs["name"])
-    self.sanbox2_fd_ip.set_text(attrs["ipaddr"])
-    self.sanbox2_fd_login.set_text(attrs["login"])
-    self.sanbox2_fd_passwd.set_text(attrs["passwd"])
-
-  def pop_bladecenter_fd(self, attrs):
-    self.bladecenter_fd_name.set_text(attrs["name"])
-    self.bladecenter_fd_ip.set_text(attrs["ipaddr"])
-    self.bladecenter_fd_login.set_text(attrs["login"])
-    self.bladecenter_fd_passwd.set_text(attrs["passwd"])
-
-  def pop_ipmilan_fd(self, attrs):
-    self.ipmilan_fd_name.set_text(attrs["name"])
-    self.ipmilan_fd_login.set_text(attrs["login"])
-    self.ipmilan_fd_passwd.set_text(attrs["passwd"])
-    self.ipmilan_fd_ip.set_text(attrs["ipaddr"])
-
-  def pop_bullpap_fd(self, attrs):
-    self.bullpap_fd_name.set_text(attrs["name"])
-    self.bullpap_fd_login.set_text(attrs["login"])
-    self.bullpap_fd_passwd.set_text(attrs["passwd"])
-    self.bullpap_fd_ip.set_text(attrs["ipaddr"])
-
-
-  def process_widgets(self):
-    ##Fence Instance Form Fields
-    self.apc_port = self.fence_xml.get_widget('entry1') 
-    self.apc_switch = self.fence_xml.get_widget('entry2') 
-    self.wti_port = self.fence_xml.get_widget('entry3') 
-    self.brocade_port = self.fence_xml.get_widget('entry4') 
-    self.vixel_port = self.fence_xml.get_widget('entry5') 
-    self.gnbd_ip = self.fence_xml.get_widget('entry6') 
-    self.ilo_port = self.fence_xml.get_widget('entry7') 
-    self.sanbox2_port = self.fence_xml.get_widget('entry8') 
-    self.bladecenter_blade = self.fence_xml.get_widget('entry41') 
-    self.mcdata_port = self.fence_xml.get_widget('entry9') 
-    self.egenera_lpan = self.fence_xml.get_widget('entry10') 
-    self.egenera_pserver = self.fence_xml.get_widget('entry11')
-    self.bullpap_domain = self.fence_xml.get_widget('entry51') 
-
-    ##Fence Device Forms
-    self.apc_fd_name = self.fence_xml.get_widget('entry12')
-    self.apc_fd_ip = self.fence_xml.get_widget('entry13')
-    self.apc_fd_login = self.fence_xml.get_widget('entry14')
-    self.apc_fd_passwd = self.fence_xml.get_widget('entry15')
-
-    self.wti_fd_ip = self.fence_xml.get_widget('entry17')
-    self.wti_fd_name = self.fence_xml.get_widget('entry16')
-    self.wti_fd_passwd = self.fence_xml.get_widget('entry18')
-
-    self.brocade_fd_name = self.fence_xml.get_widget('entry19')
-    self.brocade_fd_ip = self.fence_xml.get_widget('entry20')
-    self.brocade_fd_login = self.fence_xml.get_widget('entry21')
-    self.brocade_fd_passwd = self.fence_xml.get_widget('entry22')
-
-    self.vixel_fd_name = self.fence_xml.get_widget('entry23')
-    self.vixel_fd_ip = self.fence_xml.get_widget('entry24')
-    self.vixel_fd_passwd = self.fence_xml.get_widget('entry25')
-
-    self.gnbd_fd_name = self.fence_xml.get_widget('entry26')
-    self.gnbd_fd_server = self.fence_xml.get_widget('entry27')
-
-    self.ilo_fd_name = self.fence_xml.get_widget('entry28')
-    self.ilo_fd_login = self.fence_xml.get_widget('entry29')
-    self.ilo_fd_passwd = self.fence_xml.get_widget('entry30')
-    self.ilo_fd_hostname = self.fence_xml.get_widget('entry31')
-
-    self.sanbox2_fd_name = self.fence_xml.get_widget('entry32')
-    self.sanbox2_fd_ip = self.fence_xml.get_widget('entry33')
-    self.sanbox2_fd_login = self.fence_xml.get_widget('entry46')
-    self.sanbox2_fd_passwd = self.fence_xml.get_widget('entry47')
-
-    self.bladecenter_fd_name = self.fence_xml.get_widget('entry42')
-    self.bladecenter_fd_ip = self.fence_xml.get_widget('entry43')
-    self.bladecenter_fd_login = self.fence_xml.get_widget('entry44')
-    self.bladecenter_fd_passwd = self.fence_xml.get_widget('entry45')
-
-    self.mcdata_fd_name = self.fence_xml.get_widget('entry34')
-    self.mcdata_fd_ip = self.fence_xml.get_widget('entry35')
-    self.mcdata_fd_login = self.fence_xml.get_widget('entry36')
-    self.mcdata_fd_passwd = self.fence_xml.get_widget('entry37')
-
-    self.egenera_fd_name = self.fence_xml.get_widget('entry38')
-    self.egenera_fd_cserver = self.fence_xml.get_widget('entry39')
-
-    self.manual_fd_name = self.fence_xml.get_widget('entry40')
-
-    self.ipmilan_fd_name = self.fence_xml.get_widget('entry55')
-    self.ipmilan_fd_ip = self.fence_xml.get_widget('entry48')
-    self.ipmilan_fd_login = self.fence_xml.get_widget('entry49')
-    self.ipmilan_fd_passwd = self.fence_xml.get_widget('entry50')
-
-    self.bullpap_fd_name = self.fence_xml.get_widget('entry56')
-    self.bullpap_fd_ip = self.fence_xml.get_widget('entry52')
-    self.bullpap_fd_login = self.fence_xml.get_widget('entry53')
-    self.bullpap_fd_passwd = self.fence_xml.get_widget('entry54')
-
-  #####  Validation Methods
-  def validate_fencedevice(self, agent_type, name=None):
-    try:
-      args = list()
-      args.append(name)
-      returnlist = apply(self.fd_validate[agent_type], args)
-    except ValidationError, e:
-      MessageLibrary.errorMessage(e.getMessage())
-      return None
-
-    return returnlist
-
-    
-  def val_apc_fd(self, name):
-    rectify_fence_name = False
-    if self.apc_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.apc_fd_name)
-    if name != self.apc_fd_name.get_text():
-      res = self.check_unique_fd_name(self.apc_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-    
-    if self.apc_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-    if self.apc_fd_login.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-    if self.apc_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.apc_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.apc_fd_name.get_text()
-    fields["ipaddr"] = self.apc_fd_ip.get_text()
-    fields["login"] = self.apc_fd_login.get_text()
-    fields["passwd"] = self.apc_fd_passwd.get_text()
-
-    return fields
- 
- 
-  def val_wti_fd(self, name):
-    rectify_fence_name = False
-    if self.wti_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.wti_fd_name)
-    if name != self.wti_fd_name.get_text():
-      res = self.check_unique_fd_name(self.wti_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.wti_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-    if self.wti_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.wti_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.wti_fd_name.get_text()
-    fields["ipaddr"] = self.wti_fd_ip.get_text()
-    fields["passwd"] = self.wti_fd_passwd.get_text()
-
-    return fields
- 
- 
-  def val_brocade_fd(self, name):
-    rectify_fence_name = False
-    if self.brocade_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.brocade_fd_name)
-    if name != self.brocade_fd_name.get_text():
-      res = self.check_unique_fd_name(self.brocade_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.brocade_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-    if self.brocade_fd_login.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-    if self.brocade_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.brocade_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.brocade_fd_name.get_text()
-    fields["ipaddr"] = self.brocade_fd_ip.get_text()
-    fields["login"] = self.brocade_fd_login.get_text()
-    fields["passwd"] = self.brocade_fd_passwd.get_text()
-
-    return fields
- 
- 
-  def val_ilo_fd(self, name):
-    rectify_fence_name = False
-    if self.ilo_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.ilo_fd_name)
-    if name != self.ilo_fd_name.get_text():
-      res = self.check_unique_fd_name(self.ilo_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.ilo_fd_login.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-    if self.ilo_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-    if self.ilo_fd_hostname.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_HOSTNAME)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.ilo_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.ilo_fd_name.get_text()
-    fields["hostname"] = self.ilo_fd_hostname.get_text()
-    fields["login"] = self.ilo_fd_login.get_text()
-    fields["passwd"] = self.ilo_fd_passwd.get_text()
-
-    return fields
- 
- 
-  def val_vixel_fd(self, name):
-    rectify_fence_name = False
-    if self.vixel_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.vixel_fd_name)
-    if name != self.vixel_fd_name.get_text():
-      res = self.check_unique_fd_name(self.vixel_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.vixel_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-    if self.vixel_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.vixel_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.vixel_fd_name.get_text()
-    fields["ipaddr"] = self.vixel_fd_ip.get_text()
-    fields["passwd"] = self.vixel_fd_passwd.get_text()
-
-    return fields
- 
- 
-  def val_mcdata_fd(self, name):
-    rectify_fence_name = False
-    if self.mcdata_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.mcdata_fd_name)
-    if name != self.mcdata_fd_name.get_text():
-      res = self.check_unique_fd_name(self.mcdata_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.mcdata_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-    if self.mcdata_fd_login.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-    if self.mcdata_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.mcdata_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.mcdata_fd_name.get_text()
-    fields["ipaddr"] = self.mcdata_fd_ip.get_text()
-    fields["login"] = self.mcdata_fd_login.get_text()
-    fields["passwd"] = self.mcdata_fd_passwd.get_text()
-
-    return fields
- 
-  def val_manual_fd(self, name):
-    rectify_fence_name = False
-    if self.manual_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.manual_fd_name)
-    if name != self.manual_fd_name.get_text():
-      res = self.check_unique_fd_name(self.manual_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.manual_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.manual_fd_name.get_text()
-
-    return fields
- 
-  def val_gnbd_fd(self, name):
-    rectify_fence_name = False
-    if self.gnbd_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.gnbd_fd_name)
-    if name != self.gnbd_fd_name.get_text():
-      res = self.check_unique_fd_name(self.gnbd_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.gnbd_fd_server.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_SERVER)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.gnbd_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.gnbd_fd_name.get_text()
-    fields["server"] = self.gnbd_fd_server.get_text()
-
-    return fields
- 
-  def val_egenera_fd(self, name):
-    rectify_fence_name = False
-    if self.egenera_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.egenera_fd_name)
-    if name != self.egenera_fd_name.get_text():
-      res = self.check_unique_fd_name(self.egenera_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.egenera_fd_cserver.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_CSERVER)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.egenera_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.egenera_fd_name.get_text()
-    fields["cserver"] = self.egenera_fd_cserver.get_text()
-
-    return fields
-
- 
-  def val_sanbox2_fd(self, name):
-    rectify_fence_name = False
-    if self.sanbox2_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.sanbox2_fd_name)
-    if name != self.sanbox2_fd_name.get_text():
-      res = self.check_unique_fd_name(self.sanbox2_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.sanbox2_fd_ip.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_IP)
-
-    if self.sanbox2_fd_login.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-
-    if self.sanbox2_fd_passwd.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.sanbox2_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.sanbox2_fd_name.get_text()
-    fields["ipaddr"] = self.sanbox2_fd_ip.get_text()
-    fields["login"] = self.sanbox2_fd_login.get_text()
-    fields["passwd"] = self.sanbox2_fd_passwd.get_text()
-
-    return fields
-
-  def val_bladecenter_fd(self, name):
-    rectify_fence_name = False
-    if self.bladecenter_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.bladecenter_fd_name)
-    if name != self.bladecenter_fd_name.get_text():
-      res = self.check_unique_fd_name(self.bladecenter_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.bladecenter_fd_ip.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_IP)
-
-    if self.bladecenter_fd_login.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-
-    if self.bladecenter_fd_passwd.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.bladecenter_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.bladecenter_fd_name.get_text()
-    fields["ipaddr"] = self.bladecenter_fd_ip.get_text()
-    fields["login"] = self.bladecenter_fd_login.get_text()
-    fields["passwd"] = self.bladecenter_fd_passwd.get_text()
-
-    return fields
-
-  def val_ipmilan_fd(self, name):
-    rectify_fence_name = False
-    if self.ipmilan_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.ipmilan_fd_name)
-    if name != self.ipmilan_fd_name.get_text():
-      res = self.check_unique_fd_name(self.ipmilan_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.ipmilan_fd_login.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-    if self.ipmilan_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-    if self.ipmilan_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.ipmilan_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.ipmilan_fd_name.get_text()
-    fields["ipaddr"] = self.ipmilan_fd_ip.get_text()
-    fields["login"] = self.ipmilan_fd_login.get_text()
-    fields["passwd"] = self.ipmilan_fd_passwd.get_text()
-
-    return fields
- 
- 
-  def val_bullpap_fd(self, name):
-    rectify_fence_name = False
-    if self.bullpap_fd_name.get_text() == "":
-      raise ValidationError('FATAL', FD_PROVIDE_NAME)
-    self.validateNCName(self.bullpap_fd_name)
-    if name != self.bullpap_fd_name.get_text():
-      res = self.check_unique_fd_name(self.bullpap_fd_name.get_text())
-      if res == False:  #name is already used
-        raise ValidationError('FATAL', FD_PROVIDE_NAME)
-      rectify_fence_name = True
-
-    if self.bullpap_fd_login.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_LOGIN)
-    if self.bullpap_fd_passwd.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_PASSWD)
-    if self.bullpap_fd_ip.get_text() == "":
-        raise ValidationError('FATAL', FD_PROVIDE_IP)
-
-    if rectify_fence_name == True:
-      self.model_builder.rectifyNewFencedevicenameWithFences(name,self.bullpap_fd_name.get_text())
-
-    fields = {}
-    fields["name"] = self.bullpap_fd_name.get_text()
-    fields["ipaddr"] = self.bullpap_fd_ip.get_text()
-    fields["login"] = self.bullpap_fd_login.get_text()
-    fields["passwd"] = self.bullpap_fd_passwd.get_text()
-
-    return fields
- 
- 
-  #Validation Methods for Fence Instances 
-  def validate_fenceinstance(self, agent_type):
-    try:
-      returnlist = apply(self.fi_validate[agent_type])
-    except ValidationError, e:
-      MessageLibrary.errorMessage(e.getMessage())
-      return None
-
-    return returnlist
-
-  def val_apc(self):
-    if self.apc_port.get_text() == "": 
-      raise ValidationError('FATAL', FI_PROVIDE_PORT)
-    if self.apc_switch.get_text() == "": 
-      raise ValidationError('FATAL', FI_PROVIDE_SWITCH)
-
-    fields = {}
-    fields["port"] = self.apc_port.get_text()
-    fields["switch"] = self.apc_switch.get_text()
-
-    return fields
-
-  def val_wti(self):
-    if self.wti_port.get_text() == "":
-      raise ValidationError('FATAL', FI_PROVIDE_PORT)
-
-    fields = {}
-    fields["port"] = self.wti_port.get_text()
-
-    return fields
-
-  def val_brocade(self):
-    if self.brocade_port.get_text() == "":
-      raise ValidationError('FATAL', FI_PROVIDE_PORT)
-
-    fields = {}
-    fields["port"] = self.brocade_port.get_text()
-
-    return fields
-
-  def val_vixel(self):
-    if self.vixel_port.get_text() == "": 
-      raise ValidationError('FATAL', FI_PROVIDE_PORT)
-
-    fields = {}
-    fields["port"] = self.vixel_port.get_text()
-
-    return fields
-
-  def val_gnbd(self):
-    if self.gnbd_ip.get_text() == "": 
-      raise ValidationError('FATAL', FI_PROVIDE_IPADDRESS)
-
-    fields = {}
-    fields["ipaddress"] = self.gnbd_ip.get_text()
-
-    return fields
-
-  def val_ilo(self):
-
-    fields = {}
-
-    return fields
-
-  def val_sanbox2(self):
-    if self.sanbox2_port.get_text() == "": 
-      raise ValidationError('FATAL', FI_PROVIDE_PORT)
-
-    fields = {}
-    fields["port"] = self.sanbox2_port.get_text()
-
-    return fields
-
-  def val_bladecenter(self):
-    if self.bladecenter_blade.get_text() == "": 
-      raise ValidationError('FATAL', FI_PROVIDE_BLADE)
-
-    fields = {}
-    fields["blade"] = self.bladecenter_blade.get_text()
-
-    return fields
-
-  def val_mcdata(self):
-    if self.mcdata_port.get_text() == "":
-      raise ValidationError('FATAL', FI_PROVIDE_PORT)
-
-    fields = {}
-    fields["port"] = self.mcdata_port.get_text()
-
-    return fields
-
-  def val_egenera(self):
-    if self.egenera_lpan.get_text() == "":
-      raise ValidationError('FATAL', FI_PROVIDE_ELPAN)
-    if self.egenera_pserver.get_text() == "":
-      raise ValidationError('FATAL', FI_PROVIDE_EPSERVER)
-
-    fields = {}
-    fields["lpan"] = self.egenera_lpan.get_text()
-    fields["pserver"] = self.egenera_pserver.get_text()
-
-    return fields
-
-  def val_manual(self): 
-
-    fields = {}
-    return fields
-
-  def val_ipmilan(self):
-
-    fields = {}
-
-    return fields
-
-  def val_bullpap(self):
-    if self.bullpap_domain.get_text() == "":
-      raise ValidationError('FATAL', FI_PROVIDE_DOMAIN)
-
-    fields = {}
-    fields["domain"] = self.bullpap_domain.get_text()
-
-    return fields
-
-  def check_unique_fd_name(self, name):
-    fds = self.model_builder.getFenceDevices()
-    for fd in fds:
-      if fd.getName() == name:
-        return False
-
-    return True
-
-  def getFENCE_OPTS(self):
-    return FENCE_OPTS
-
-  def set_model(self, model_builder):
-    self.model_builder = model_builder
-
-
-
-  ### name must conform to relaxNG ID type ##
-  def isNCName(self, name):
-    for ch in ILLEGAL_CHARS:
-      if ch in name:
-        return False
-    return True
-  
-  def makeNCName(self, name):
-    new_name = ''
-    for ch in name:
-      if ch in ILLEGAL_CHARS:
-        new_name = new_name + '_'
-      else:
-        new_name = new_name + ch
-    return new_name
-
-  def validateNCName(self, gtkentry):
-    name = gtkentry.get_text().strip()
-    gtkentry.set_text(name)
-    if not self.isNCName(name):
-      name = self.makeNCName(name)
-      gtkentry.set_text(name)
-      # select text
-      raise ValidationError('FATAL', ILLEGAL_CHARS_REPLACED)
-
-def validateNewFenceDevice(form, model): 
-  from FenceDevice import FenceDevice
-  try:
-    agent_type = form['fence_type']
-  except KeyError, e:
-    return (FD_VAL_FAIL, "No agent type in form submission")
-
-  ##Now that we have an agent type, we should check the fencedev name
-  ##before wasting any time checking other fields.
-  try:
-    fencedev_name = form['name']
-    fencedev_name = fencedev_name.strip()
-  except KeyError, e:
-    return (FD_VAL_FAIL, "No device name in form submission")
-
-  if fencedev_name == "":
-    return (1, "A unique name is required for every fence device")
-
-  fencedevs = model.getFenceDevices()
-  for fd in fencedevs:
-    if fd.getName().strip() == fencedev_name:
-      return (FD_VAL_FAIL, FD_PROVIDE_NAME)
-
-  if agent_type == "fence_apc":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_wti":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_brocade":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_vixel":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-
-  elif agent_type == "fence_mcdata":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-
-  elif agent_type == "fence_gnbd":
-    try:
-      server = form['server']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_SERVER)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("server",server)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_egenera":
-    try:
-      cserver = form['cserver']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_CSERVER)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("cserver",cserver)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-
-  elif agent_type == "fence_sanbox2":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_bladecenter":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_bullpap":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-
-  elif agent_type == "fence_xvm":
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  elif agent_type == "fence_scsi":
-
-    fencedev = FenceDevice()
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedevptr = model.getFenceDevicePtr()
-    fencedevptr.addChild(fencedev)
-    return (FD_VAL_SUCCESS, FD_NEW_SUCCESS % FENCE_OPTS[agent_type])
-
-  #Oh-oh...no agent match
-  else:
-    return (FD_VAL_FAIL, FD_NEW_FAIL % agent_type)
-  
-def validateFenceDevice(form, model): 
-  from FenceDevice import FenceDevice
-  namechange = False
-
-  try:
-    agent_type = form['fence_type']
-  except KeyError, e:
-    return (FD_VAL_FAIL, "No agent type in form submission")
-
-  ##Now that we have an agent type, we should check the fencedev name
-  ##before wasting any time checking other fields.
-  try:
-    fencedev_name = form['name']
-    fencedev_name = fencedev_name.strip()
-  except KeyError, e:
-    return (FD_VAL_FAIL, "No device name in form submission")
-
-  if fencedev_name == "":
-    return (1, "No device name in form submission")
-
-  try:
-    orig_name = form['orig_name']
-  except KeyError, e:
-    return (FD_VAL_FAIL, "Cannot retrieve original fence device")
-
-  if orig_name != fencedev_name:
-    namechange = True
-
-    fencedevs = model.getFenceDevices()
-    for fd in fencedevs:
-      if fd.getName().strip() == fencedev_name:
-        return (FD_VAL_FAIL, FD_PROVIDE_NAME)
-  else:
-    fencedevs = model.getFenceDevices()
-
-  #Now we know name is unique...find device now
-  fencedev = None
-  for fd in fencedevs:
-    if fd.getName().strip() == orig_name:
-      fencedev = fd
-      break
-
-  if fencedev == None:
-    return (FD_VAL_FAIL, "Could not find fencedevice in current configuration")
-
-  if agent_type == "fence_apc":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_wti":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_brocade":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_vixel":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-
-  elif agent_type == "fence_mcdata":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-
-  elif agent_type == "fence_gnbd":
-    try:
-      server = form['server']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_SERVER)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("server",server)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_egenera":
-    try:
-      cserver = form['cserver']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_CSERVER)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("cserver",cserver)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-
-  elif agent_type == "fence_sanbox2":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_bladecenter":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_bullpap":
-    try:
-      ip = form['ipaddr']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_IP)
-    try:
-      log = form['login']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_LOGIN)
-    try:
-      pwd = form['passwd']
-    except KeyError, e:
-      return (FD_VAL_FAIL, FD_PROVIDE_PASSWD)
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    fencedev.addAttribute("ipaddr",ip)
-    fencedev.addAttribute("login",log)
-    fencedev.addAttribute("passwd",pwd)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-
-  elif agent_type == "fence_xvm":
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  elif agent_type == "fence_scsi":
-
-    fencedev.addAttribute("agent",agent_type)
-    fencedev.addAttribute("name",fencedev_name)
-    if namechange:
-      model.rectifyNewFencedevicenameWithFences(orig_name,fencedev_name)
-    return (FD_VAL_SUCCESS, FD_UPDATE_SUCCESS % orig_name)
-
-  #Oh-oh...no agent match
-  else:
-    return (FD_VAL_FAIL, FD_NEW_FAIL % agent_type)
+	return (FD_VAL_SUCCESS, fenceinst)
--- conga/luci/site/luci/Extensions/Fs.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/Fs.py	2007/04/12 17:19:18	1.1.4.1
@@ -6,7 +6,7 @@
 _ = gettext.gettext
 
 TAG_NAME = "fs"
-RESOURCE_TYPE = _("File System: ")
+RESOURCE_TYPE = _("File System")
 
 class Fs(BaseResource):
   def __init__(self):
--- conga/luci/site/luci/Extensions/Ip.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/Ip.py	2007/04/12 17:19:18	1.1.4.1
@@ -6,7 +6,7 @@
 _ = gettext.gettext
 
 TAG_NAME = "ip"
-RESOURCE_TYPE=_("IP Address: ")
+RESOURCE_TYPE=_("IP Address")
 
 class Ip(BaseResource):
   def __init__(self):
--- conga/luci/site/luci/Extensions/ModelBuilder.py	2007/01/15 21:29:09	1.8.2.6
+++ conga/luci/site/luci/Extensions/ModelBuilder.py	2007/04/12 17:19:18	1.8.2.6.2.1
@@ -17,6 +17,7 @@
 from Method import Method
 from Device import Device
 from Cman import Cman
+from Totem import Totem
 from Gulm import Gulm
 from Lockserver import Lockserver
 from Ip import Ip
@@ -25,8 +26,15 @@
 from NFSExport import NFSExport
 from Fs import Fs
 from Samba import Samba
+from Apache import Apache
+from LVM import LVM
+from MySQL import MySQL
+from OpenLDAP import OpenLDAP
+from Postgres8 import Postgres8
+from Tomcat5 import Tomcat5
 from Multicast import Multicast
 from FenceDaemon import FenceDaemon
+from FenceXVMd import FenceXVMd
 from Netfs import Netfs
 from Clusterfs import Clusterfs
 from Resources import Resources
@@ -40,11 +48,9 @@
 from FailoverDomainNode import FailoverDomainNode
 from Rm import Rm
 from Heuristic import Heuristic
-from CommandHandler import CommandHandler
-from CommandError import CommandError
 from GeneralError import GeneralError
 from clui_constants import *
-import MessageLibrary
+
 TAGNAMES={ 'cluster':Cluster,
            'clusternodes':ClusterNodes,
            'clusternode':ClusterNode,
@@ -53,11 +59,13 @@
            'fencedevices':FenceDevices,
            'method':Method,
            'cman':Cman,
+           'totem':Totem,
            'gulm':Gulm,
            'lockserver':Lockserver,
            'rm':Rm,
            'service':Service,
            'vm':Vm,
+           'fence_xvmd':FenceXVMd,
            'resources':Resources,
            'failoverdomain':FailoverDomain,
            'failoverdomains':FailoverDomains,
@@ -65,6 +73,12 @@
            'ip':Ip,
            'fs':Fs,
            'smb':Samba,
+           'apache':Apache,
+           'lvm':LVM,
+           'mysql':MySQL,
+           'openldap':OpenLDAP,
+           'postgres-8':Postgres8,
+           'tomcat-5':Tomcat5,
            'fence_daemon':FenceDaemon,
            'multicast':Multicast,
            'clusterfs':Clusterfs,
@@ -87,14 +101,16 @@
 FENCEDAEMON_PTR_STR="fence_daemon"
 SERVICE="service"
 VM="vm"
+FENCE_XVMD_STR="fence_xvmd"
 GULM_TAG_STR="gulm"
 MCAST_STR="multicast"
 CMAN_PTR_STR="cman"
+TOTEM_PTR_STR="totem"
 QUORUMD_PTR_STR="quorumd"
 ###-----------------------------------
 
 
-INVALID_GULM_COUNT="GuLM locking mechanism may consist of 1, 3, 4 or 5 locking servers. You have configured %d. Fix the error and try saving again."
+INVALID_GULM_COUNT="GULM locking mechanism may consist of 1, 3, or 5 locking servers. You have configured %d. Fix the error and try saving again."
 
 
 class ModelBuilder:
@@ -112,16 +128,17 @@
     self.cluster_ptr = None
     self.GULM_ptr = None
     self.CMAN_ptr = None
+    self.TOTEM_ptr = None
     self.clusternodes_ptr = None
     self.failoverdomains_ptr = None
     self.fencedevices_ptr = None
     self.resourcemanager_ptr = None
     self.resources_ptr = None
     self.fence_daemon_ptr = None
-    self.command_handler = CommandHandler()
     self.isModified = False
     self.quorumd_ptr = None
     self.usesQuorumd = False
+    self.fence_xvmd_ptr = None
     self.unusual_items = list()
     self.isVirtualized = False
     if mcast_addr == None:
@@ -218,8 +235,12 @@
         self.lock_type = GULM_TYPE
       elif parent_node.nodeName == CMAN_PTR_STR:
         self.CMAN_ptr = new_object
+      elif parent_node.nodeName == TOTEM_PTR_STR:
+        self.TOTEM_ptr = new_object
       elif parent_node.nodeName == MCAST_STR:
         self.usesMulticast = True
+      elif parent_node.nodeName == FENCE_XVMD_STR:
+        self.fence_xvmd_ptr = new_object
 
     else:
       return None
@@ -280,15 +301,13 @@
 
     obj_tree.addAttribute("name","alpha_cluster")
     obj_tree.addAttribute("config_version","1")
-    fdp = FenceDaemon()
-    obj_tree.addChild(fdp)
-    self.fence_daemon_ptr = fdp
     cns = ClusterNodes()
     obj_tree.addChild(cns)
     self.clusternodes_ptr = cns
 
     gulm = Gulm()
     self.GULM_ptr = gulm
+    self.fence_daemon_ptr = None
     obj_tree.addChild(gulm)
 
     fds = FenceDevices()
@@ -521,9 +540,6 @@
   def getFilepath(self):
     return self.filename
 
-  def isClusterMember(self):
-    return self.command_handler.isClusterMember()
-
   def setIsVirtualized(self,isVirtualized):
     if isVirtualized == None:
       self.isVirtualized = False
@@ -549,7 +565,7 @@
   def deleteNode(self, clusternode):
     #1) delete node
     #2) delete failoverdomainnodes with same name
-    #3) delete lockserver nodes if GuLM
+    #3) delete lockserver nodes if GULM
 
     name = clusternode.getName()
 
@@ -591,13 +607,40 @@
 
     raise GeneralError('FATAL',"Couldn't find service name in current list")
 
-  def retrieveXenVMsByName(self, name):
-    vms = self.getXENVMs()
+  def retrieveVMsByName(self, name):
+    vms = self.getVMs()
     for v in vms:
       if v.getName() == name:
         return v
 
-    raise GeneralError('FATAL',"Couldn't find xen vm name %s in current node list" % name)
+    raise GeneralError('FATAL',"Couldn't find VM name %s in current list" % name)
+
+  def del_totem(self):
+    if self.TOTEM_ptr is not None:
+      self.cluster_ptr.removeChild(self.TOTEM_ptr)
+      self.TOTEM_ptr = None
+
+  def add_totem(self, obj):
+    self.del_totem()
+    if self.TOTEM_ptr is None:
+      self.cluster_ptr.addChild(obj)
+      self.TOTEM_ptr = obj
+
+  def hasFenceXVM(self):
+    return self.fence_xvmd_ptr is not None
+
+  # Right now the fence_xvmd tag is empty, but allow the object
+  # to be passed in case attributes are added in the future.
+  def addFenceXVM(self, obj):
+    if self.fence_xvmd_ptr is not None:
+      self.cluster_ptr.removeChild(self.fence_xvmd_ptr)
+    self.cluster_ptr.addChild(obj)
+    self.fence_xvmd_ptr = obj
+
+  def delFenceXVM(self):
+    if self.fence_xvmd_ptr is not None:
+      self.cluster_ptr.removeChild(self.fence_xvmd_ptr)
+      self.fence_xvmd_ptr = None
 
   def getFenceDevices(self):
     if self.fencedevices_ptr == None:
@@ -613,10 +656,17 @@
       return list()
     else:
       return self.failoverdomains_ptr.getChildren()
-        
+
   def getFailoverDomainPtr(self):
     return self.failoverdomains_ptr
 
+  def getFailoverDomainByName(self, fdom_name):
+    fdoms = self.getFailoverDomains()
+    for i in fdoms:
+      if i.getName() == fdom_name:
+        return i
+    return None
+
   def getFailoverDomainsForNode(self, nodename):
     matches = list()
     faildoms = self.getFailoverDomains()
@@ -707,7 +757,7 @@
       self.resourcemanager_ptr.addChild(rcs)
       self.resources_ptr = rcs
         
-    if self.fence_daemon_ptr == None:
+    if self.GULM_ptr is None and self.fence_daemon_ptr is None:
       fdp = FenceDaemon()
       self.cluster_ptr.addChild(fdp)
       self.fence_daemon_ptr = fdp
@@ -730,7 +780,7 @@
 
     return None
         
-  def getXENVMs(self):
+  def getVMs(self):
     rg_list = list()
     if self.resourcemanager_ptr != None:
       kids = self.resourcemanager_ptr.getChildren()
@@ -775,6 +825,12 @@
   def getGULMPtr(self):
     return self.GULM_ptr
 
+  def getCMANPtr(self):
+    return self.CMAN_ptr
+
+  def getTotemPtr(self):
+    return self.TOTEM_ptr
+
   def getLockServer(self, name):
     children = self.GULM_ptr.getChildren()
     for child in children:
@@ -792,7 +848,7 @@
 
   def isNodeLockserver(self,name):
     gptr = self.getGULMPtr()
-    if gptr == None:  #Obviously not GuLM
+    if gptr == None:  #Obviously not GULM
       return False
     children = gptr.getChildren()
     for child in children:
@@ -803,7 +859,7 @@
 
   def removeLockserver(self, clusternode):
     gptr = self.getGULMPtr()
-    if gptr == None:  #Obviously not GuLM
+    if gptr == None:  #Obviously not GULM
       return
     children = gptr.getChildren()
     for child in children:
@@ -818,6 +874,8 @@
     if self.lock_type == DLM_TYPE:
       #remove <cman>
       self.cluster_ptr.removeChild(self.CMAN_ptr)
+      if self.TOTEM_ptr is not None:
+        self.del_totem()
       self.CMAN_ptr = None
 
       #add gulm tag
@@ -938,7 +996,7 @@
     
 
   def check_fence_daemon(self):
-    if self.fence_daemon_ptr == None:
+    if self.GULM_ptr is None and self.fence_daemon_ptr is None:
       self.fence_daemon_ptr = FenceDaemon()
       self.cluster_ptr.addChild(self.fence_daemon_ptr)
 
@@ -1031,8 +1089,7 @@
   def check_gulm_count(self):
     if self.getLockType() == GULM_TYPE:
       gulm_count = len(self.getGULMPtr().getChildren())
-      if not (gulm_count in (1, 3, 4, 5)):
-        MessageLibrary.errorMessage(INVALID_GULM_COUNT % gulm_count)
+      if not (gulm_count in (1, 3, 5)):
         return False
     return True
 
@@ -1107,7 +1164,11 @@
         if found_one == True:
           break
           
-    
+  def searchObjectTree(self, tagtype):
+    objlist = list()
+    self.object_tree.searchTree(objlist, tagtype)
+
+    return objlist
  
    
 if __name__ == "__main__":
--- conga/luci/site/luci/Extensions/NFSClient.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/NFSClient.py	2007/04/12 17:19:18	1.1.6.1
@@ -5,7 +5,7 @@
 import gettext
 _ = gettext.gettext
 
-RESOURCE_TYPE=_("NFS Client: ")
+RESOURCE_TYPE=_("NFS Client")
 TAG_NAME = "nfsclient"
 DENY_ALL_CHILDREN = True
 
--- conga/luci/site/luci/Extensions/NFSExport.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/NFSExport.py	2007/04/12 17:19:18	1.1.4.1
@@ -5,7 +5,7 @@
 import gettext
 _ = gettext.gettext
 
-RESOURCE_TYPE = _("NFS Export: ")
+RESOURCE_TYPE = _("NFS Export")
 TAG_NAME = "nfsexport"
 
 class NFSExport(BaseResource):
--- conga/luci/site/luci/Extensions/Netfs.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/Netfs.py	2007/04/12 17:19:18	1.1.4.1
@@ -6,7 +6,7 @@
 _ = gettext.gettext
 
 TAG_NAME = "netfs"
-RESOURCE_TYPE = _("NFS Mount: ")
+RESOURCE_TYPE = _("NFS Mount")
 
 class Netfs(BaseResource):
   def __init__(self):
--- conga/luci/site/luci/Extensions/Samba.py	2006/07/20 21:30:33	1.1
+++ conga/luci/site/luci/Extensions/Samba.py	2007/04/12 17:19:18	1.1.4.1
@@ -6,7 +6,7 @@
 _ = gettext.gettext
 
 TAG_NAME = "smb"
-RESOURCE_TYPE = _("Samba Service: ")
+RESOURCE_TYPE = _("Samba Service")
 
 class Samba(BaseResource):
   def __init__(self):
--- conga/luci/site/luci/Extensions/Script.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/Script.py	2007/04/12 17:19:18	1.1.4.1
@@ -5,7 +5,7 @@
 import gettext
 _ = gettext.gettext
 
-RESOURCE_TYPE = _("Script: ")
+RESOURCE_TYPE = _("Script")
 TAG_NAME = "script"
 
 class Script(BaseResource):
--- conga/luci/site/luci/Extensions/StorageReport.py	2006/12/08 18:27:32	1.20.2.2
+++ conga/luci/site/luci/Extensions/StorageReport.py	2007/04/12 17:19:18	1.20.2.2.2.1
@@ -1409,13 +1409,23 @@
                 # clvmd error
                 error  = True
                 url   += '?' + STONAME + '=' + storagename + '&' + PAGETYPE + '=' + STORAGE
-                msg    = 'either clvmd (clustered LVM daemon) is not running or is not cluster-enabled on ' + storagename
+                msg    = 'clvmd (clustered LVM daemon) is not running on ' + storagename
             elif code == 5:
                 # not quorate
                 error  = True
                 url   += '?' + STONAME + '=' + storagename + '&' + PAGETYPE + '=' + STORAGE
                 msg    = 'Cluster quorum is required, and yet cluster is not quorate. Start cluster, and try again.'
-            elif code > 5:
+            elif code == 6:
+                # LVM cluster locking not enabled
+                error  = True
+                url   += '?' + STONAME + '=' + storagename + '&' + PAGETYPE + '=' + STORAGE
+                msg    = 'LVM cluster locking is not enabled on ' + storagename
+            elif code == 7:
+                # cluster not running
+                error  = True
+                url   += '?' + STONAME + '=' + storagename + '&' + PAGETYPE + '=' + STORAGE
+                msg    = 'Cluster infrastructure is not running on ' + storagename
+            elif code > 8:
                 error  = True
                 url   += '?' + STONAME + '=' + storagename + '&' + PAGETYPE + '=' + STORAGE
                 msg    = err_msg
--- conga/luci/site/luci/Extensions/TagObject.py	2007/01/10 21:48:26	1.1.2.1
+++ conga/luci/site/luci/Extensions/TagObject.py	2007/04/12 17:19:18	1.1.2.1.2.1
@@ -78,3 +78,12 @@
 
   def isRefObject(self):
     return False
+
+  def searchTree(self, objlist, tagtype):
+    if self.TAG_NAME == tagtype:
+      objlist.append(self)
+    if len(self.children) > 0:
+      for child in self.children:
+        if child == None:
+          continue
+        child.searchTree(objlist, tagtype)
--- conga/luci/site/luci/Extensions/clui_constants.py	2006/05/30 20:17:21	1.1
+++ conga/luci/site/luci/Extensions/clui_constants.py	2007/04/12 17:19:18	1.1.4.1
@@ -66,7 +66,7 @@
 
 XML_CONFIG_ERROR=_("A problem was encountered while reading configuration file %s . Details or the error appear below. Click the \'Cancel\' button to quit the application. Click the \'New\' button to create a new configuration file. To continue anyway (Not Recommended!), click the \'Ok\' button.") 
 
-SWITCH_TO_GULM=_("Change to GuLM Lockserver")
+SWITCH_TO_GULM=_("Change to GULM Lockserver")
 SWITCH_TO_DLM=_("Change to Distributed Lock Manager")
 SWITCH_TO_BROADCAST=_("Change to Broadcast Mode for Cluster Manager")
 SWITCH_TO_MULTICAST=_("Change to Multicast Mode for Cluster Manager")
--- conga/luci/site/luci/Extensions/cluster_adapters.py	2007/01/17 22:26:27	1.120.2.20
+++ conga/luci/site/luci/Extensions/cluster_adapters.py	2007/04/12 17:19:18	1.120.2.20.2.1
@@ -11,23 +11,33 @@
 from Ip import Ip
 from Clusterfs import Clusterfs
 from Fs import Fs
+from FailoverDomain import FailoverDomain
+from FailoverDomainNode import FailoverDomainNode
 from RefObject import RefObject
 from ClusterNode import ClusterNode
 from NFSClient import NFSClient
 from NFSExport import NFSExport
 from Service import Service
+from Lockserver import Lockserver
 from Netfs import Netfs
+from Apache import Apache
+from MySQL import MySQL
+from Postgres8 import Postgres8
+from Tomcat5 import Tomcat5
+from OpenLDAP import OpenLDAP
 from Vm import Vm
+from FenceXVMd import FenceXVMd
 from Script import Script
 from Samba import Samba
+from LVM import LVM
 from QuorumD import QuorumD
 from Heuristic import Heuristic
 from clusterOS import resolveOSType
 from Fence import Fence
 from Method import Method
-from FenceDevice import FenceDevice
+from Totem import Totem
 from Device import Device
-from FenceHandler import validateNewFenceDevice, FENCE_OPTS, validateFenceDevice
+from FenceHandler import validateNewFenceDevice, FENCE_OPTS, validateFenceDevice, validate_fenceinstance
 from GeneralError import GeneralError
 from homebase_adapters import manageCluster, createClusterSystems, havePermCreateCluster, setNodeFlag, delNodeFlag, userAuthenticated, getStorageNode, getClusterNode, delCluster, parseHostForm
 from LuciSyslog import LuciSyslog
@@ -45,6 +55,27 @@
 except:
 	pass
 
+def get_fsid_list(model):
+	obj_list = model.searchObjectTree('fs')
+	obj_list.extend(model.searchObjectTree('clusterfs'))
+	return map(lambda x: x.getAttribute('fsid') and int(x.getAttribute('fsid')) or 0, obj_list)
+
+def fsid_is_unique(model, fsid):
+	fsid_list = get_fsid_list(model)
+	return fsid not in fsid_list
+
+def generate_fsid(model, name):
+	import binascii
+	from random import random
+	fsid_list = get_fsid_list(model)
+
+	fsid = binascii.crc32(name) & 0xffff
+	dupe = fsid in fsid_list
+	while dupe is True:
+		fsid = (fsid + random.randrange(1, 0xfffe)) & 0xffff
+		dupe = fsid in fsid_list
+	return fsid
+
 def buildClusterCreateFlags(self, batch_map, clusterName):
 	path = str(CLUSTER_FOLDER_PATH + clusterName)
 
@@ -119,7 +150,7 @@
 
 	system_list, incomplete, errors, messages = parseHostForm(request, check_certs)
 	add_cluster['nodes'] = system_list
-	
+
 	for i in system_list:
 		cur_system = system_list[i]
 
@@ -150,13 +181,13 @@
 			try:
 				if prev_auth:
 					messages.append('Host %s is already authenticated.' \
-						% cur_host) 
+						% cur_host)
 				else:
 					rc.auth(cur_passwd)
 
 				if not rc.authed():
 					raise Exception, 'authentication failed'
-			except:
+			except Exception, e:
 				cur_system['errors'] = True
 				incomplete = True
 				errors.append('Error authenticating to %s: %s' \
@@ -227,6 +258,49 @@
 	add_cluster, incomplete, errors, messages = parseClusterNodes(self, request, cluster_os)
 	clusterName = add_cluster['name']
 
+	if len(clusterName) > 15:
+		errors.append('A cluster\'s name must be less than 16 characters long.')
+
+	try:
+		cluster_os = add_cluster['cluster_os']
+	except:
+		pass
+
+	lockmanager = 'dlm'
+	if cluster_os == 'rhel4':
+		add_cluster['gulm_support'] = True
+		if not request.form.has_key('lockmanager'):
+			# The user hasn't been presented with the RHEL4
+			# lock manager options yet.
+			incomplete = True
+		else:
+			try:
+				lockmanager = request.form['lockmanager'].strip()
+			except:
+				lockmanager = 'dlm'
+
+	lockservers = None
+	if lockmanager == 'gulm':
+		add_cluster['lockmanager'] = 'gulm'
+		try:
+			lockservers = filter(lambda x: x.strip(), request.form['__GULM__'])
+			if not lockservers or len(lockservers) < 1:
+				raise Exception, 'blank'
+			num_lockservers = len(lockservers)
+			if not num_lockservers in (1, 3, 5):
+				errors.append('You must have exactly 1, 3, or 5 GULM lock servers. You submitted %d lock servers.' % num_lockservers)
+		except:
+			errors.append('No lock servers were given.')
+
+		if len(errors) > 0:
+			try:
+				ls_hash = {}
+				for i in xrange(num_lockservers):
+					ls_hash['server%d' % (i + 1)] = lockservers[i]
+				add_cluster['gulm_lockservers'] = ls_hash
+			except:
+				pass
+
 	if incomplete or len(errors) > 0:
 		request.SESSION.set('create_cluster', add_cluster)
 		return (False, { 'errors': errors, 'messages': messages })
@@ -240,7 +314,8 @@
 					True,
 					add_cluster['shared_storage'],
 					False,
-					add_cluster['download_pkgs'])
+					add_cluster['download_pkgs'],
+					lockservers)
 
 	if not batchNode:
 		request.SESSION.set('create_cluster', add_cluster)
@@ -258,7 +333,7 @@
 		try:
 			rc = RicciCommunicator(i)
 			if not rc:
-				raise 'rc is None'
+				raise Exception, 'rc is None'
 		except Exception, e:
 			msg = 'Unable to connect to the ricci agent on %s: %s' % (i, str(e))
 			errors.append(msg)
@@ -271,7 +346,9 @@
 		try:
 			resultNode = rc.process_batch(batchNode, async=True)
 			batch_id_map[i] = resultNode.getAttribute('batch_id')
-		except:
+		except Exception, e:
+			luci_log.debug_verbose('validateCreateCluster0: %s: %s' \
+				% (i, str(e)))
 			errors.append('An error occurred while attempting to add cluster node \"%s\"' % i)
 			if len(batch_id_map) == 0:
 				request.SESSION.set('create_cluster', add_cluster)
@@ -327,7 +404,7 @@
 		except Exception, e:
 			luci_log.debug_verbose('VACN1: %s: %s' % (clusterName, str(e)))
 			return (False, { 'errors': [ 'The database object for %s is missing.' % clusterName ] })
-		
+
 		try:
 			cluster_os = cluster_folder.manage_getProperty('cluster_os')
 			if not cluster_os:
@@ -369,7 +446,7 @@
 
 	system_list, incomplete, errors, messages = parseHostForm(request, check_certs)
 	add_cluster['nodes'] = system_list
-	
+
 	for i in system_list:
 		cur_system = system_list[i]
 
@@ -396,16 +473,17 @@
 
 			prev_auth = rc.authed()
 			cur_system['prev_auth'] = prev_auth
+
 			try:
 				if prev_auth:
 					messages.append('Host %s is already authenticated.' \
-						% cur_host) 
+						% cur_host)
 				else:
 					rc.auth(cur_passwd)
 
 				if not rc.authed():
 					raise Exception, 'authentication failed'
-			except:
+			except Exception, e:
 				cur_system['errors'] = True
 				incomplete = True
 				errors.append('Error authenticating to %s: %s' \
@@ -508,7 +586,8 @@
 								True,
 								shared_storage,
 								False,
-								download_pkgs)
+								download_pkgs,
+								model.GULM_ptr is not None)
 				if not batch_node:
 					raise Exception, 'batch is blank'
 				system_list[x]['batch'] = batch_node
@@ -540,8 +619,6 @@
 			request.SESSION.set('add_node', add_cluster)
 			return (False, { 'errors': errors, 'messages': messages })
 
-		cp = model.getClusterPtr()
-		cp.incrementConfigVersion()
 		model.setModified(True)
 		conf_str = str(model.exportModelAsString())
 		if not conf_str:
@@ -566,7 +643,7 @@
 				break
 			if code == -1:
 				errors.append(batch_ret[1])
-				raise Exception, batch_ret[1]
+				raise Exception, str(batch_ret[1])
 			if code == False:
 				time.sleep(0.5)
 	except Exception, e:
@@ -618,7 +695,7 @@
 
 		if not success:
 			incomplete = True
-			errors.append('An error occurred while attempting to add cluster node \"%s\"')
+			errors.append('An error occurred while attempting to add cluster node \"%s\"' % cur_host)
 
 	if incomplete or len(errors) > 0:
 		request.SESSION.set('add_node', add_cluster)
@@ -724,6 +801,25 @@
 	if len(errors) > 0:
 		return (False, {'errors': errors})
 
+	fdom = None
+	try:
+		fdom = request.form['domain'].strip()
+		if not fdom:
+			raise Exception, 'blank'
+	except:
+		fdom = None
+
+	recovery = None
+	try:
+		recovery = request.form['recovery'].strip()
+		if not recovery:
+			recovery = None
+		else:
+			if recovery != 'restart' and recovery != 'relocate' and recovery != 'disable':
+				errors.append('You entered an invalid recovery option: \"%s\" Valid options are \"restart\" \"relocate\" and \"disable\"')
+	except:
+		recovery = None
+
 	try:
 		service_name = request.form['svc_name'].strip()
 	except Exception, e:
@@ -738,6 +834,15 @@
 		autostart = None
 		luci_log.debug_verbose('vSA5a: error getting autostart: %s' % str(e))
 
+	exclusive = "0"
+	try:
+		if not request.form.has_key('exclusive') or request.form['exclusive'] != "1":
+			exclusive = "0"
+		else:
+			exclusive = "1"
+	except Exception, e:
+		exclusive = "0"
+
 	try:
 		cur_service = model.retrieveServiceByName(service_name)
 	except GeneralError, e:
@@ -776,6 +881,11 @@
 
 	new_service = Service()
 	new_service.addAttribute('name', service_name)
+	if fdom:
+		new_service.addAttribute('domain', fdom)
+	if recovery:
+		new_service.addAttribute('recovery', recovery)
+	new_service.addAttribute('exclusive', str(exclusive))
 	if autostart is not None:
 		new_service.attr_hash['autostart'] = autostart
 
@@ -788,8 +898,6 @@
 		return (False, {'errors': [ 'Unable to determine cluster name' ]})
 
 	try:
-		cp = model.getClusterPtr()
-		cp.incrementConfigVersion()
 		model.setModified(True)
 		conf = model.exportModelAsString()
 		if not conf:
@@ -855,11 +963,18 @@
 		return (False, {'errors': errors})
 
 	return (True, {'messages': ['Resource added successfully']})
-	
+
 ## Cluster properties form validation routines
 
 # rhel5 cluster version
 def validateMCastConfig(model, form):
+	try:
+		gulm_ptr = model.getGULMPtr()
+		if gulm_ptr:
+			return (False, {'errors': ['Multicast cannot be used with GULM locking.']})
+	except:
+		pass
+
 	errors = list()
 	try:
 		mcast_val = form['mcast'].strip().lower()
@@ -919,8 +1034,16 @@
 	except KeyError, e:
 		return (False, {'errors': ['An invalid quorum partition selection was made']})
 
+	cp = model.getClusterPtr()
+	qdp = model.getQuorumdPtr()
+
 	if not qdisk_val:
-		return (True, {'messages': ['Changes accepted. - FILL ME IN']})
+		if qdp:
+			try:
+				cp.removeChild(qdp)
+			except Exception, e:
+				return (False, {'errors': [ 'Error disabling quorum partition: %s' % str(e) ] })
+		return (True, {})
 
 	try:
 		interval = int(form['interval'])
@@ -960,22 +1083,25 @@
 
 	#Either device or label must be present
 	device = None
-	label = None
 	try:
 		device = form['device'].strip()
+	except:
+		device = None
+
+	label = None
+	try:
 		label = form['label'].strip()
-		if not device and not label:
-			raise KeyError, 'device and label are both none'
-	except KeyError, e:
+	except:
+		label = None
+
+	if not device and not label:
 		errors.append('No Device or Label value was given')
 
 	num_heuristics = 0
 	try:
-		num_heuristics = int(form['num_heuristics'])
-		if num_heuristics < 0:
-			raise ValueError, 'invalid number of heuristics: %s' % form['num_heuristics']
-		if num_heuristics == 0:
-			num_heuristics = 1
+		num_heuristics = int(form['num_heuristics']) + 1
+		if num_heuristics < 1:
+			raise ValueError, form['num_heuristics']
 	except KeyError, e:
 		errors.append('No number of heuristics was given.')
 	except ValueError, e:
@@ -983,65 +1109,67 @@
 
 	heuristics = list()
 	for i in xrange(num_heuristics):
-		prefix = 'heuristic' + str(i) + ':'
 		try:
-			hname = form[prefix + 'hname'].strip()
-			if not hname:
-				raise KeyError(prefix + 'hname')
-		except KeyError, e:
-			if ((not prefix + 'hpath' in form or not form['hpath'].strip()) and
-				(not prefix + 'hint' in form or not form['hint'].strip()) and
-				(not prefix + 'hscore' in form or not form['hscore'].strip())):
-				# The row is blank; ignore it.
+			h = form['heuristic%d' % i]
+			if not h or len(h) != 3:
 				continue
-			errors.append('No heuristic name was given for heuristic #%d' % i + 1)
+		except:
+			continue
 
 		try:
-			hpath = form[prefix + 'hpath']
-		except KeyError, e:
-			errors.append('No heuristic path was given for heuristic #%d' % i + 1)
-
+			hprog = h[0]
+			if not hprog:
+				raise Exception, 'no hprog'
+		except Exception, e:
+			errors.append('No program was given for heuristic %d' % i + 1)
 		try:
-			hint = int(form[prefix + 'hint'])
+			hint = int(h[1])
 			if hint < 1:
 				raise ValueError, 'Heuristic interval values must be greater than 0'
 		except KeyError, e:
-			errors.append('No heuristic interval was given for heuristic #%d' % i + 1)
+			errors.append('No interval was given for heuristic #%d' % i + 1)
 		except ValueError, e:
-			errors.append('An invalid heuristic interval was given for heuristic #%d: %s' % (i + 1, str(e)))
+			errors.append('An invalid interval was given for heuristic %d: %s' \
+				% (i + 1, str(e)))
 
 		try:
-			hscore = int(form[prefix + 'score'])
+			hscore = int(h[2])
 			if hscore < 1:
 				raise ValueError, 'Heuristic scores must be greater than 0'
 		except KeyError, e:
-			errors.append('No heuristic score was given for heuristic #%d' % i + 1)
+			errors.append('No score was given for heuristic %d' % i + 1)
 		except ValueError, e:
-			errors.append('An invalid heuristic score was given for heuristic #%d: %s' % (i + 1, str(e)))
-		heuristics.append([ hname, hpath, hint, hscore ])
+			errors.append('An invalid score was given for heuristic %d: %s' \
+				% (i + 1, str(e)))
+
+		heuristics.append([ hprog, hint, hscore ])
 
 	if len(errors) > 0:
 		return (False, {'errors': errors })
 
 	qd = QuorumD()
-	qd.addAttribute('interval', interval)
-	qd.addAttribute('votes', votes)
-	qd.addAttribute('tko', tko)
-	qd.addAttribute('min_score', min_score)
+	qd.addAttribute('interval', str(interval))
+	qd.addAttribute('votes', str(votes))
+	qd.addAttribute('tko', str(tko))
+	qd.addAttribute('min_score', str(min_score))
 
 	if device:
-		qd.addAttribute('device', device)
+		qd.addAttribute('device', str(device))
 	else:
-		qd.addAttribute('label', label)
+		qd.addAttribute('label', str(label))
 
-	cp = model.getClusterPtr()
+	if qdp:
+		try:
+			cp.removeChild(qdp)
+		except:
+			pass
 	cp.addChild(qd)
 
 	for h in heuristics:
 		new_h = Heuristic()
-		new_h.addAttribute('program', h[1])
-		new_h.addAttribute('interval', h[2])
-		new_h.addAttribute('score', h[3])
+		new_h.addAttribute('program', str(h[0]))
+		new_h.addAttribute('interval', str(h[1]))
+		new_h.addAttribute('score', str(h[2]))
 		qd.addChild(new_h)
 
 	if len(errors) > 0:
@@ -1068,12 +1196,16 @@
 	except KeyError, e:
 		errors.append('No cluster name was given.')
 
+	if len(cluster_name) > 15:
+		errors.append('A cluster\'s name must be less than 16 characters long.')
+
 	try:
 		version_num = int(form['cfgver'])
 		if version_num < old_ver:
 			raise ValueError, 'configuration version number must be %d or greater.' % old_ver
-		# we'll increment the cluster version before propagating it.
-		version_num -= 1
+		if version_num != old_ver:
+			# we'll increment the cluster version before propagating it.
+			version_num -= 1
 	except KeyError, e:
 		errors.append('No cluster configuration version was given.')
 	except ValueError, e:
@@ -1088,6 +1220,85 @@
 			luci_log.debug_verbose('unable to update general properties: %s' % str(e))
 			errors.append('Unable to update the cluster configuration.')
 
+	try:
+		cluster_version = form['cluster_version'].strip()
+		if cluster_version != 'rhel5':
+			raise Exception, 'not rhel5'
+	except:
+		if len(errors) > 0:
+			return (False, {'errors': errors})
+		return (True, {})
+
+	totem = model.getTotemPtr()
+	if totem is None:
+		cp = model.getClusterPtr()
+		totem = Totem()
+		cp.addChild(totem)
+
+	try:
+		token = form['token'].strip()
+		if not token:
+			raise KeyError, 'token'
+		token = int(token)
+		if token < 1:
+			raise ValueError, '%d is an invalid value for token timeout' % token
+		totem.addAttribute('token', str(token))
+	except KeyError, e:
+		try:
+			totem.removeAttribute('token')
+		except:
+			pass
+	except Exception, e:
+		errors.append(str(e))
+
+	try:
+		token_retransmits_before_loss_const = form['token_retransmits_before_loss_const'].strip()
+		if not token_retransmits_before_loss_const:
+			raise KeyError, 'token_retransmits_before_loss_const'
+		token_retransmits_before_loss_const = int(token_retransmits_before_loss_const)
+		if token_retransmits_before_loss_const < 1:
+			raise ValueError, '%d is an invalid value for number of token retransmits before loss' % token_retransmits_before_loss_const
+		totem.addAttribute('token_retransmits_before_loss_const', str(token_retransmits_before_loss_const))
+	except KeyError, e:
+		try:
+			totem.removeAttribute('token_retransmits_before_loss_const')
+		except:
+			pass
+	except Exception, e:
+		errors.append(str(e))
+
+	try:
+		join = form['join'].strip()
+		if not join:
+			raise KeyError, 'join'
+		join = int(join)
+		if join < 1:
+			raise ValueError, '%d is an invalid value for join timeout' % join
+		totem.addAttribute('join', str(join))
+	except KeyError, e:
+		try:
+			totem.removeAttribute('join')
+		except:
+			pass
+	except Exception, e:
+		errors.append(str(e))
+
+	try:
+		consensus = form['consensus'].strip()
+		if not consensus:
+			raise KeyError, 'consensus'
+		consensus = int(consensus)
+		if consensus < 1:
+			raise ValueError, '%d is an invalid value for consensus timeout' % consensus
+		totem.addAttribute('consensus', str(consensus))
+	except KeyError, e:
+		try:
+			totem.removeAttribute('consensus')
+		except:
+			pass
+	except Exception, e:
+		errors.append(str(e))
+
 	if len(errors) > 0:
 		return (False, {'errors': errors})
 	return (True, {})
@@ -1095,6 +1306,9 @@
 def validateFenceConfig(model, form):
 	errors = list()
 
+	if model.getGULMPtr() is not None:
+		return (False, {'errors': [ 'GULM clusters do not support fenced.' ]})
+
 	try:
 		post_fail_delay = int(form['post_fail_delay'])
 		if post_fail_delay < 0:
@@ -1113,6 +1327,18 @@
 	except ValueError, e:
 		errors.append('Invalid post join delay: %s' % str(e))
 
+	run_xvmd = False
+	try:
+		run_xvmd = form.has_key('run_xvmd')
+	except:
+		pass
+
+	if run_xvmd is True and not model.hasFenceXVM():
+		fenceXVMd = FenceXVMd()
+		model.addFenceXVM(fenceXVMd)
+	elif not run_xvmd:
+		model.delFenceXVM()
+
 	try:
 		fd = model.getFenceDaemonPtr()
 		old_pj_delay = fd.getPostJoinDelay()
@@ -1132,11 +1358,47 @@
 
 	return (True, {})
 
+def validateGULMConfig(model, form):
+	gulm_ptr = model.getGULMPtr()
+	if not gulm_ptr:
+		return (False, {'errors': [ 'This cluster appears not to be using GULM locking.' ]})
+
+	node_list = map(lambda x: x.getName(), gulm_ptr.getChildren())
+	for i in map(lambda x: x.getName(), model.getNodes()):
+		if not i in node_list:
+			node_list.append(i)
+
+	gulm_lockservers = list()
+	for node in node_list:
+		if form.has_key(node) and form[node] == 'on':
+			ls = Lockserver()
+			ls.addAttribute('name', node)
+			gulm_lockservers.append(ls)
+
+	try:
+		xlockservers = filter(lambda x: x.strip(), form['__GULM__'])
+	except:
+		xlockservers = list()
+
+	for i in xlockservers:
+		if not i in node_list:
+			ls = Lockserver()
+			ls.addAttribute('name', i)
+			gulm_lockservers.append(ls)
+
+	num_ls = len(gulm_lockservers)
+	if not num_ls in (1, 3, 5):
+		return (False, {'errors': [ 'You must have exactly 1, 3, or 5 GULM lock servers. You submitted %d lock servers.' % num_ls ]})
+
+	model.GULM_ptr.children = gulm_lockservers
+	return (True, {})
+
 configFormValidators = {
 	'general': validateGeneralConfig,
 	'mcast': validateMCastConfig,
 	'fence': validateFenceConfig,
-	'qdisk': validateQDiskConfig
+	'qdisk': validateQDiskConfig,
+	'gulm': validateGULMConfig
 }
 
 def validateConfigCluster(self, request):
@@ -1215,7 +1477,7 @@
       raise Exception, 'cluster name from model.getClusterName() is blank'
   except Exception, e:
     luci_log.debug_verbose('VCC5: error: getClusterName: %s' % str(e))
-    errors.append('Unable to determine cluster name from model') 
+    errors.append('Unable to determine cluster name from model')
 
   if len(errors) > 0:
     return (retcode, {'errors': errors, 'messages': messages})
@@ -1251,7 +1513,7 @@
   errors = list()
   messages = list()
   rc = None
-                                                                                
+
   try:
     model = request.SESSION.get('model')
     if not model:
@@ -1266,12 +1528,12 @@
       except:
         luci_log.debug_verbose('VFE: no model, no cluster name')
         return (False, {'errors': ['No cluster model was found.']})
-                                                                                
+
     try:
       model = getModelForCluster(self, cluname)
     except:
       model = None
-                                                                                
+
     if model is None:
       luci_log.debug_verbose('VFE: unable to get model from session')
       return (False, {'errors': ['No cluster model was found.']})
@@ -1290,20 +1552,15 @@
     luci_log.debug_verbose('VFE: no form was submitted')
     return (False, {'errors': ['No form was submitted']})
 
-  #fencehandler = FenceHandler()
-  error_code, error_string = validateNewFenceDevice(form, model)
+  error_code, retobj = validateNewFenceDevice(form, model)
   if error_code == FD_VAL_SUCCESS:
-    messages.append(error_string)
     try:
-      cp = model.getClusterPtr()
-      cp.incrementConfigVersion()
-      model.setModified(True)
       conf_str = model.exportModelAsString()
       if not conf_str:
         raise Exception, 'conf_str is none'
     except Exception, e:
       luci_log.debug_verbose('VFE: export model as string failed: %s' \
-      % str(e))
+        % str(e))
       errors.append('Unable to store the new cluster configuration')
 
     try:
@@ -1312,7 +1569,7 @@
         raise Exception, 'cluster name from model.getClusterName() is blank'
     except Exception, e:
       luci_log.debug_verbose('VFA: error: getClusterName: %s' % str(e))
-      errors.append('Unable to determine cluster name from model') 
+      errors.append('Unable to determine cluster name from model')
 
     if not rc:
       rc = getRicciAgent(self, clustername)
@@ -1330,14 +1587,13 @@
       else:
         try:
           set_node_flag(self, clustername, rc.hostname(), batch_id,
-          CLUSTER_CONFIG, 'Updating cluster configuration')
+            CLUSTER_CONFIG, 'Adding new fence device \"%s\"' % retobj)
         except:
           pass
 
-    response.redirect(request['URL'] + "?pagetype=" + FENCEDEV + "&clustername=" + clustername + "&fencename=" + form['name'] + '&busyfirst=true')
-    return (True, {'errors': errors, 'messages': messages})
+    response.redirect(request['URL'] + "?pagetype=" + FENCEDEV + "&clustername=" + clustername + "&fencename=" + retobj + '&busyfirst=true')
   else:
-    errors.append(error_string)
+    errors.extend(retobj)
     return (False, {'errors': errors, 'messages': messages})
 
 
@@ -1345,7 +1601,7 @@
   errors = list()
   messages = list()
   rc = None
-                                                                                
+
   try:
     model = request.SESSION.get('model')
     if not model:
@@ -1360,12 +1616,12 @@
       except:
         luci_log.debug_verbose('VFE: no model, no cluster name')
         return (False, {'errors': ['No cluster model was found.']})
-                                                                                
+
     try:
       model = getModelForCluster(self, cluname)
     except:
       model = None
-                                                                                
+
     if model is None:
       luci_log.debug_verbose('VFE: unable to get model from session')
       return (False, {'errors': ['No cluster model was found.']})
@@ -1388,19 +1644,15 @@
   #entry for this fence device.
   #
   #pass form and model to validation method, then save changes if it passes.
-  error_code, error_string = validateFenceDevice(form, model)
+  error_code, retobj = validateFenceDevice(form, model)
   if error_code == FD_VAL_SUCCESS:
-    messages.append(error_string)
     try:
-      cp = model.getClusterPtr()
-      cp.incrementConfigVersion()
-      model.setModified(True)
       conf_str = model.exportModelAsString()
       if not conf_str:
         raise Exception, 'conf_str is none'
     except Exception, e:
       luci_log.debug_verbose('VFE: export model as string failed: %s' \
-      % str(e))
+        % str(e))
       errors.append('Unable to store the new cluster configuration')
 
     try:
@@ -1409,35 +1661,36 @@
         raise Exception, 'cluster name from model.getClusterName() is blank'
     except Exception, e:
       luci_log.debug_verbose('VFA: error: getClusterName: %s' % str(e))
-      errors.append('Unable to determine cluster name from model') 
+      errors.append('Unable to determine cluster name from model')
 
     if not rc:
       rc = getRicciAgent(self, clustername)
       if not rc:
         luci_log.debug_verbose('VFA: unable to find a ricci agent for the %s cluster' % clustername)
         errors.append('Unable to contact a ricci agent for cluster %s' \
-        % clustername)
+          % clustername)
 
     if rc:
       batch_id, result = setClusterConf(rc, str(conf_str))
       if batch_id is None or result is None:
         luci_log.debug_verbose('VFA: setClusterConf: batchid or result is None')
         errors.append('Unable to propagate the new cluster configuration for %s' \
-        % clustername)
+          % clustername)
       else:
         try:
           set_node_flag(self, clustername, rc.hostname(), batch_id,
-          CLUSTER_CONFIG, 'Updating cluster configuration')
+            CLUSTER_CONFIG, 'Updating fence device \"%s\"' % retobj)
         except:
           pass
 
-    response.redirect(request['URL'] + "?pagetype=" + FENCEDEV + "&clustername=" + clustername + "&fencename=" + request['fencename'] + '&busyfirst=true')
-    return (True, {'errors': errors, 'messages': messages})
+    response.redirect(request['URL'] + "?pagetype=" + FENCEDEV + "&clustername=" + clustername + "&fencename=" + retobj + '&busyfirst=true')
   else:
-    errors.append(error_string)
+    errors.extend(retobj)
     return (False, {'errors': errors, 'messages': messages})
 
 def validateNodeFenceConfig(self, request):
+	errors = list()
+
 	try:
 		form_xml = request['fence_xml']
 		if not form_xml:
@@ -1519,10 +1772,10 @@
 				break
 		if delete_target is not None:
 			try:
-				node.getChildren()[0].removeChild(l)
+				node.getChildren()[0].removeChild(delete_target)
 			except Exception, e:
 				luci_log.debug_verbose('vNFC6a: %s: %s' % (method_id, str(e)))
-				return (False, {'errors': ['An error occurred while deleting fence method %s' % method_id ]}) 
+				return (False, {'errors': ['An error occurred while deleting fence method %s' % method_id ]})
 		else:
 			return (True, {'messages': ['No changes were made.'] })
 
@@ -1563,7 +1816,7 @@
 				return (False, {'errors': [ 'Unable to determine what device the current instance uses.' ]})
 
 			try:
-				parent_form = form_hash[parent][1].append(dummy_form)
+				form_hash[parent][1].append(dummy_form)
 				del dummy_form['fence_instance']
 			except Exception, e:
 				luci_log.debug_verbose('vNFC10: no parent for instance')
@@ -1576,7 +1829,6 @@
 	for i in fh_keys:
 		fencedev_name = None
 		fencedev_unknown = False
-		fencedev_obj = None
 
 		try:
 			fence_form, instance_list = form_hash[i]
@@ -1588,140 +1840,70 @@
 			fence_type = fence_form['fence_type']
 			if not fence_type:
 				raise Exception, 'fence type is blank'
-			fence_form['agent'] = fence_type
 		except Exception, e:
 			luci_log.debug_verbose('vNFC12: %s %s' % (i, str(e)))
 			fence_type = None
 
-		try:
-			del fence_form['fence_type']
-		except:
-			pass
-
 		if 'existing_device' in fence_form:
-			del fence_form['existing_device']
-
 			try:
 				fencedev_name = fence_form['name']
 				if not fencedev_name.strip():
 					raise Exception, 'no fence name'
 			except Exception, e:
-				return (False, {'errors': [ 'You must provide a unique name for all fence devices.' ]})
+				errors.append('You must provide a unique name for all fence devices.')
+				continue
 
 			if fence_type is None:
-				# An unknown device. Pull the data out of
+				# An unknown fence device agent. Pull the data out of
 				# the model and persist it and all instances.
 				# All we care about is its name.
 				fencedev_unknown = True
 			else:
-				if 'sharable' in fence_form:
+				if not 'sharable' in fence_form:
 					# If it's a shared fence device that already exists, the
 					# user could not have edited it (without playing dirty
 					# games), so it's safe to pull the existing entry from
-					# the model. All we need is the device name.
-					del fence_form['sharable']
-				else:
-					# An existing non-shared device; build up the device
-					# from scratch since the user could have edited it.
-					try:
-						old_name = fence_form['old_name']
-						if not old_name:
-							raise Exception, 'old name is blank'
-						del fence_form['old_name']
-					except Exception, e:
-						luci_log.debug_verbose('vNFC12: no old name for %s %s' \
-							% (fence_form['name'], str(e)))
-						return (False, {'errors': [ 'Unable to determine the original name for the device now named %s' % fencedev_name ]})
-
-					fencedev_obj = None
-					fence_dev_list = model.getFenceDevices()
-					for fd in fence_dev_list:
-						if fd.getAttribute('name') == old_name:
-							fencedev_obj = fd
-							break
-
-					if fencedev_obj is None:
-						luci_log.debug_verbose('vNFC14: no fence device named %s was found' % old_name)
-						return (False, {'errors': ['No fence device named %s was found' % old_name ] })
+					# the model. All we need is the device name, and nothing
+					# else needs to be done here.
+					#
+					# For an existing non-shared device update the device
+					# in the model, since the user could have edited it.
+					retcode, retmsg = validateFenceDevice(fence_form, model)
+					if retcode != FD_VAL_SUCCESS:
+						errors.extend(retmsg)
+						continue
 					else:
-						try:
-							model.fencedevices_ptr.removeChild(fd)
-						except Exception, e:
-							luci_log.debug_verbose('VNFC8a: %s: %s' \
-								% (old_name, str(e)))
-							return (False, {'errors': [ 'Unable to remove old fence device %s' % old_name ]})
-
-					for k in fence_form.keys():
-						if fence_form[k]:
-							fencedev_obj.addAttribute(k, str(fence_form[k]))
+						fencedev_name = retmsg
 
 					# Add back the tags under the method block
 					# for the fence instance
 					instance_list.append({'name': fencedev_name })
 		else:
 			# The user created a new fence device.
-			try:
-				fencedev_name = fence_form['name']
-				if not fencedev_name.strip():
-					raise Exception, 'no fence name'
-			except Exception, e:
-				return (False, {'errors': [ 'You must provide a unique name for all fence devices.' ]})
-
-			fencedev_obj = FenceDevice()
-			for k in fence_form.keys():
-				if fence_form[k]:
-					fencedev_obj.addAttribute(k, str(fence_form[k]))
+			retcode, retmsg = validateNewFenceDevice(fence_form, model)
+			if retcode != FD_VAL_SUCCESS:
+				errors.extend(retmsg)
+				continue
+			else:
+				fencedev_name = retmsg
 
 			# If it's not shared, we need to create an instance form
 			# so the appropriate XML goes into the <method> block inside
 			# <node><fence>. All we need for that is the device name.
 			if not 'sharable' in fence_form:
 				instance_list.append({'name': fencedev_name })
-			else:
-				del fence_form['sharable']
-
-		if fencedev_obj is not None:
-			# If a device with this name exists in the model
-			# already, replace it with the current object. If
-			# this block is not executed, we don't need to make
-			# any changes to the fencedevices block for this
-			# device
-			fence_dev_list = model.getFenceDevices()
-			for fd in fence_dev_list:
-				if fencedev_name == fd.getAttribute('name'):
-					luci_log.debug_verbose('vNFC15: fence ident %s already in use' % fencedev_name)
-					return (False, {'errors': ['There is already a fence device named %s' % fencedev_name ] })
-			model.fencedevices_ptr.addChild(fencedev_obj)
 
 		if fencedev_unknown is True:
 			# Save any instances for this fence device.
+			# XXX FIX ME - instances must be saved.
 			pass
 
 		for inst in instance_list:
-			try:
-				del inst['parent_fencedev']
-			except:
-				pass
-			try:
-				del inst['new_instance']
-			except:
-				pass
-			try:
-				del inst['name']
-			except:
-				pass
-			try:
-				del inst['existing_instance']
-			except:
-				pass
-
-			device_obj = Device()
-			device_obj.setAgentType(fence_type)
-			device_obj.addAttribute('name', fencedev_name)
-			for k in inst.keys():
-				if inst[k]:
-					device_obj.addAttribute(k, str(inst[k]))
-			fence_method.addChild(device_obj)
+			retcode, retobj = validate_fenceinstance(inst, fencedev_name, fence_type)
+			if retcode != FD_VAL_SUCCESS:
+				errors.extend(retobj)
+				continue
+			fence_method.addChild(retobj)
 
 		if len(node.getChildren()) > 0:
 			# There's already a <fence> block
@@ -1741,9 +1923,10 @@
 			fence_node.addChild(fence_method)
 			node.addChild(fence_node)
 
+	if len(errors) > 0:
+		return (False, {'errors': errors })
+
 	try:
-		cp = model.getClusterPtr()
-		cp.incrementConfigVersion()
 		model.setModified(True)
 		conf = str(model.exportModelAsString())
 		if not conf:
@@ -1777,7 +1960,7 @@
   errors = list()
   messages = list()
   rc = None
-                                                                                
+
   try:
     model = request.SESSION.get('model')
     if not model:
@@ -1792,12 +1975,12 @@
       except:
         luci_log.debug_verbose('VFE: no model, no cluster name')
         return (False, {'errors': ['No cluster model was found.']})
-                                                                                
+
     try:
       model = getModelForCluster(self, cluname)
     except:
       model = None
-                                                                                
+
     if model is None:
       luci_log.debug_verbose('VFE: unable to get model from session')
       return (False, {'errors': ['No cluster model was found.']})
@@ -1844,18 +2027,16 @@
   except:
     error_code = FD_VAL_FAIL
     error_string = "Fence device %s could not be removed from configuration" % fencedev_name
- 
+
   try:
     model.removeFenceInstancesForFenceDevice(fencedev_name)
   except:
     luci_log.debug_verbose('VFD: Could not remove fence instances for')
-     
+
 
   if error_code == FD_VAL_SUCCESS:
     messages.append(error_string)
     try:
-      cp = model.getClusterPtr()
-      cp.incrementConfigVersion()
       model.setModified(True)
       conf_str = model.exportModelAsString()
       if not conf_str:
@@ -1871,7 +2052,7 @@
         raise Exception, 'cluster name from model.getClusterName() is blank'
     except Exception, e:
       luci_log.debug_verbose('VFA: error: getClusterName: %s' % str(e))
-      errors.append('Unable to determine cluster name from model') 
+      errors.append('Unable to determine cluster name from model')
 
     if not rc:
       rc = getRicciAgent(self, clustername)
@@ -1889,7 +2070,7 @@
       else:
         try:
           set_node_flag(self, clustername, rc.hostname(), batch_id,
-          CLUSTER_CONFIG, 'Updating cluster configuration')
+            CLUSTER_CONFIG, 'Removing fence device \"%s\"' % fencedev_name)
         except:
           pass
 
@@ -1960,7 +2141,7 @@
 		luci_log.debug_verbose('VDP5: RC %s: %s' % (nodename_resolved, str(e)))
 		errors.append('Unable to connect to the ricci agent on %s to update cluster daemon properties' % nodename_resolved)
 		return (False, {'errors': errors})
-		
+
 	batch_id, result = updateServices(rc, enable_list, disable_list)
 	if batch_id is None or result is None:
 		luci_log.debug_verbose('VDP6: setCluserConf: batchid or result is None')
@@ -1982,73 +2163,372 @@
 
 	response.redirect(request['URL'] + "?pagetype=" + NODE + "&clustername=" + clustername + '&nodename=' + nodename + '&busyfirst=true')
 
-formValidators = {
-	6: validateCreateCluster,
-	7: validateConfigCluster,
-	15: validateAddClusterNode,
-	21: validateServiceAdd,
-	24: validateServiceAdd,
-	31: validateResourceAdd,
-	33: validateResourceAdd,
-	51: validateFenceAdd,
-	54: validateFenceEdit,
-	55: validateDaemonProperties,
-	57: deleteFenceDevice,
-	58: validateNodeFenceConfig
-}
+def validateFdom(self, request):
+	errors = list()
 
-def validatePost(self, request):
 	try:
-		pagetype = int(request.form['pagetype'])
+		model = request.SESSION.get('model')
+		if not model:
+			raise Exception, 'no model'
 	except Exception, e:
-		luci_log.debug_verbose('VP0: error: %s' % str(e))
-		return None
+		luci_log.debug_verbose('validateFdom0: no model: %s' % str(e))
+		return (False, {'errors': [ 'Unable to retrieve cluster information.' ]})
 
-	if not pagetype in formValidators:
-		luci_log.debug_verbose('VP1: no handler for page type %d' % pagetype)
-		return None
-	else:
-		return formValidators[pagetype](self, request)
+	prioritized = False
+	try:
+		prioritized = request.form.has_key('prioritized')
+	except:
+		prioritized = False
 
+	restricted = False
+	try:
+		restricted = request.form.has_key('restricted')
+	except:
+		restricted = False
 
-def createCluChooser(self, request, systems):
-  dummynode = {}
+	clustername = None
+	try:
+		clustername = request.form['clustername'].strip()
+		if not clustername:
+			raise Exception, 'blank'
+	except:
+		try:
+			clustername = model.getClusterName()
+			if not clustername:
+				raise Exception, 'blank'
+		except:
+			clustername = None
 
-  if request.REQUEST_METHOD == 'POST':
-    ret = validatePost(self, request)
-    try:
-      request.SESSION.set('checkRet', ret[1])
-    except:
-      request.SESSION.set('checkRet', {})
-  else:
-    try:
-      request.SESSION.set('checkRet', {})
-    except:
-      pass
+	if not clustername:
+		errors.append('Unable to determine this cluster\'s name.')
 
-  #First, see if a cluster is chosen, then
-  #check that the current user can access that system
-  cname = None
-  try:
-    cname = request[CLUNAME]
-  except:
-    cname = ""
+	try:
+		name = request.form['name'].strip()
+		if not name:
+			raise Exception, 'blank'
+	except Exception, e:
+		errors.append('No name was given for this failover domain.')
+		luci_log.debug_verbose('validateFdom0: %s' % str(e))
 
-  try:
-    url = request['URL']
-  except:
-    url = "/luci/cluster/index_html"
+	oldname = None
+	try:
+		oldname = request.form['oldname'].strip()
+		if not oldname:
+			raise Exception, 'blank'
+	except:
+		pass
 
-  try:
-    pagetype = request[PAGETYPE]
-  except:
-    pagetype = '3'
+	if oldname is None or oldname != name:
+		if model.getFailoverDomainByName(name) is not None:
+			errors.append('A failover domain named \"%s\" already exists.' % name)
+
+	fdom = None
+	if oldname is not None:
+		fdom = model.getFailoverDomainByName(oldname)
+		if fdom is None:
+			luci_log.debug_verbose('validateFdom1: No fdom named %s exists' % oldname)
+			errors.append('No failover domain named \"%s" exists.' % oldname)
+		else:
+			fdom.addAttribute('name', name)
+			fdom.children = list()
+	else:
+		fdom = FailoverDomain()
+		fdom.addAttribute('name', name)
 
+	if fdom is None or len(errors) > 0:
+		return (False, {'errors': errors })
 
-  cldata = {}
-  cldata['Title'] = "Cluster List"
-  cldata['cfg_type'] = "clusters"
-  cldata['absolute_url'] = url + "?pagetype=" + CLUSTERLIST
+	if prioritized:
+		fdom.addAttribute('ordered', '1')
+	else:
+		fdom.addAttribute('ordered', '0')
+
+	if restricted:
+		fdom.addAttribute('restricted', '1')
+	else:
+		fdom.addAttribute('restricted', '0')
+
+	cluster_nodes = map(lambda x: str(x.getName()), model.getNodes())
+
+	for i in cluster_nodes:
+		if request.form.has_key(i):
+			fdn = FailoverDomainNode()
+			fdn.addAttribute('name', i)
+			if prioritized:
+				priority = 1
+				try:
+					priority = int(request.form['__PRIORITY__' + i].strip())
+					if priority < 1:
+						priority = 1
+				except Exception, e:
+					priority = 1
+				fdn.addAttribute('priority', str(priority))
+			fdom.addChild(fdn)
+
+	try:
+		fdom_ptr = model.getFailoverDomainPtr()
+		if not oldname:
+			fdom_ptr.addChild(fdom)
+		model.setModified(True)
+		conf = str(model.exportModelAsString())
+	except Exception, e:
+		luci_log.debug_verbose('validateFdom2: %s' % str(e))
+		errors.append('Unable to update the cluster configuration.')
+
+	if len(errors) > 0:
+		return (False, {'errors': errors })
+
+	rc = getRicciAgent(self, clustername)
+	if not rc:
+		luci_log.debug_verbose('validateFdom3: unable to find a ricci agent for cluster %s' % clustername)
+		return (False, {'errors': ['Unable to find a ricci agent for the %s cluster' % clustername ]})
+	ragent = rc.hostname()
+
+	batch_number, result = setClusterConf(rc, conf)
+	if batch_number is None or result is None:
+		luci_log.debug_verbose('validateFdom4: missing batch and/or result')
+		return (False, {'errors': [ 'An error occurred while constructing the new cluster configuration.' ]})
+
+	try:
+		if oldname:
+			set_node_flag(self, clustername, ragent, str(batch_number), FDOM, 'Updating failover domain \"%s\"' % oldname)
+		else:
+			set_node_flag(self, clustername, ragent, str(batch_number), FDOM_ADD, 'Creating failover domain \"%s\"' % name)
+	except Exception, e:
+		luci_log.debug_verbose('validateFdom5: failed to set flags: %s' % str(e))
+
+	response = request.RESPONSE
+	response.redirect(request['URL'] + "?pagetype=" + FDOM + "&clustername=" + clustername + '&fdomname=' + name + '&busyfirst=true')
+
+def validateVM(self, request):
+	errors = list()
+
+	model = request.SESSION.get('model')
+
+	try:
+		vm_name = request.form['vmname'].strip()
+		if not vm_name:
+			raise Exception, 'blank'
+	except Exception, e:
+		luci_log.debug_verbose('validateVM0: no vm name: %s' % str(e))
+		errors.append('No virtual machine name was given.')
+
+	try:
+		vm_path = request.form['vmpath'].strip()
+		if not vm_path:
+			raise Exception, 'blank'
+	except Exception, e:
+		luci_log.debug_verbose('validateVM1: no vm path: %s' % str(e))
+		errors.append('No path to the virtual machine configuration file was given.')
+
+	autostart = 1
+	try:
+		if request.form.has_key('autostart'):
+			autostart = 1
+		else:
+			autostart = 0
+	except:
+		autostart = 1
+
+	exclusive = 0
+	try:
+		if request.form.has_key('exclusive'):
+			exclusive = 1
+		else:
+			exclusive = 0
+	except:
+		exclusive = 0
+
+	recovery = None
+	try:
+		recovery = request.form['recovery'].strip()
+		if not recovery:
+			recovery = None
+		else:
+			if recovery != 'restart' and recovery != 'relocate' and recovery != 'disable':
+				errors.append('You entered an invalid recovery option: \"%s\" Valid options are \"restart\" \"relocate\" and \"disable\"')
+	except:
+		recovery = None
+
+	fdom = None
+	try:
+		fdom = request.form['domain'].strip()
+		if not fdom:
+			raise Exception, 'blank'
+	except:
+		fdom = None
+
+	if len(errors) > 0:
+		return (False, {'errors': errors })
+
+	isNew = False
+	try:
+		old_name = request.form['oldname'].strip()
+		if not old_name:
+			raise KeyError, 'oldname'
+	except KeyError, e:
+		isNew = True
+
+	delete_vm = False
+	if request.form.has_key('delete'):
+		try:
+			xvm = model.retrieveVMsByName(old_name)
+			if not xvm:
+				raise Exception, 'not found'
+			rmptr = model.getResourceManagerPtr()
+			rmptr.removeChild(xvm)
+			delete_vm = True
+		except:
+			return (False, {'errors': ['No virtual machine service named \"%s\" exists.' % old_name ]})
+	else:
+		if isNew is True:
+			xvm = Vm()
+			xvm.addAttribute('name', vm_name)
+			xvm.addAttribute('path', vm_path)
+			rmptr = model.getResourceManagerPtr()
+			rmptr.addChild(xvm)
+		else:
+			try:
+				xvm = model.retrieveVMsByName(old_name)
+				if not xvm:
+					raise Exception, 'not found'
+			except:
+				return (False, {'errors': ['No virtual machine service named \"%s\" exists.' % old_name ]})
+			xvm.addAttribute('name', vm_name)
+			xvm.addAttribute('path', vm_path)
+
+	xvm.addAttribute('autostart', str(autostart))
+	xvm.addAttribute('exclusive', str(exclusive))
+	if fdom:
+		xvm.addAttribute('domain', fdom)
+	else:
+		try:
+			xvm.removeAttribute('domain')
+		except:
+			pass
+
+	if recovery:
+		xvm.addAttribute('recovery', recovery)
+	else:
+		try:
+			xvm.removeAttribute('recovery')
+		except:
+			pass
+
+	try:
+		model.setModified(True)
+		stringbuf = str(model.exportModelAsString())
+		if not stringbuf:
+			raise Exception, 'model is blank'
+	except Exception, e:
+		luci_log.debug_verbose('validateVM2: %s' % str(e))
+		errors.append('Unable to update the cluster model')
+
+	try:
+		clustername = model.getClusterName()
+		if not clustername:
+			raise Exception, 'cluster name from model.getClusterName() is blank'
+	except Exception, e:
+		luci_log.debug_verbose('validateVM3: %s' % str(e))
+		errors.append('Unable to determine the cluster name.')
+
+	if len(errors) > 0:
+		return (False, {'errors': errors })
+
+	rc = getRicciAgent(self, clustername)
+	if not rc:
+		luci_log.debug_verbose('validateVM4: no ricci for %s' % clustername)
+		return (False, {'errors': ['Unable to contact a ricci agent for this cluster.']})
+
+	batch_number, result = setClusterConf(rc, stringbuf)
+	if batch_number is None or result is None:
+		luci_log.debug_verbose('validateVM5: missing batch and/or result')
+		return (False, {'errors': [ 'Error creating virtual machine %s.' % vm_name ]})
+
+	try:
+		if delete_vm is True:
+			set_node_flag(self, clustername, rc.hostname(), str(batch_number), VM_CONFIG, "Deleting virtual machine service \'%s\'" % vm_name)
+		elif isNew is True:
+			set_node_flag(self, clustername, rc.hostname(), str(batch_number), VM_ADD, "Creating virtual machine service \'%s\'" % vm_name)
+		else:
+			set_node_flag(self, clustername, rc.hostname(), str(batch_number), VM_CONFIG, "Configuring virtual machine service \'%s\'" % vm_name)
+	except Exception, e:
+		luci_log.debug_verbose('validateVM6: failed to set flags: %s' % str(e))
+
+	response = request.RESPONSE
+	response.redirect(request['URL'] + "?pagetype=" + SERVICES + "&clustername=" + clustername + '&busyfirst=true')
+
+formValidators = {
+	6: validateCreateCluster,
+	7: validateConfigCluster,
+	15: validateAddClusterNode,
+	18: validateVM,
+	19: validateVM,
+	21: validateServiceAdd,
+	24: validateServiceAdd,
+	31: validateResourceAdd,
+	33: validateResourceAdd,
+	41: validateFdom,
+	44: validateFdom,
+	51: validateFenceAdd,
+	54: validateFenceEdit,
+	55: validateDaemonProperties,
+	57: deleteFenceDevice,
+	58: validateNodeFenceConfig
+}
+
+def validatePost(self, request):
+	try:
+		pagetype = int(request.form['pagetype'])
+	except Exception, e:
+		luci_log.debug_verbose('VP0: error: %s' % str(e))
+		return None
+
+	if not pagetype in formValidators:
+		luci_log.debug_verbose('VP1: no handler for page type %d' % pagetype)
+		return None
+	else:
+		return formValidators[pagetype](self, request)
+
+
+def createCluChooser(self, request, systems):
+  dummynode = {}
+
+  if request.REQUEST_METHOD == 'POST':
+    ret = validatePost(self, request)
+    try:
+      request.SESSION.set('checkRet', ret[1])
+    except:
+      request.SESSION.set('checkRet', {})
+  else:
+    try:
+      request.SESSION.set('checkRet', {})
+    except:
+      pass
+
+  #First, see if a cluster is chosen, then
+  #check that the current user can access that system
+  cname = None
+  try:
+    cname = request[CLUNAME]
+  except:
+    cname = ""
+
+  try:
+    url = request['URL']
+  except:
+    url = "/luci/cluster/index_html"
+
+  try:
+    pagetype = request[PAGETYPE]
+  except:
+    pagetype = '3'
+
+
+  cldata = {}
+  cldata['Title'] = "Cluster List"
+  cldata['cfg_type'] = "clusters"
+  cldata['absolute_url'] = url + "?pagetype=" + CLUSTERLIST
   cldata['Description'] = "Clusters available for configuration"
   if pagetype == CLUSTERLIST:
     cldata['currentItem'] = True
@@ -2115,12 +2595,11 @@
   return dummynode
 
 def getnodes(self, model):
-  mb = model
-  nodes = mb.getNodes()
-  names = list()
-  for node in nodes:
-    names.append(node.getName())
-  return names
+	try:
+		return map(lambda x: str(x.getName()), model.getNodes())
+	except Exception, e:
+		luci_log.debug_verbose('getnodes0: %s' % str(e))
+	return []
 
 def createCluConfigTree(self, request, model):
   dummynode = {}
@@ -2226,11 +2705,11 @@
   sv['cfg_type'] = "services"
   sv['absolute_url'] = url + "?pagetype=" + SERVICES + "&clustername=" + cluname
   sv['Description'] = "Service configuration for this cluster"
-  if pagetype == SERVICES or pagetype == SERVICE_CONFIG or pagetype == SERVICE_ADD or pagetype == SERVICE:
+  if pagetype == SERVICES or pagetype == SERVICE_CONFIG or pagetype == SERVICE_ADD or pagetype == SERVICE or pagetype == SERVICE_LIST or pagetype == VM_ADD or pagetype == VM_CONFIG:
     sv['show_children'] = True
   else:
     sv['show_children'] = False
-  if pagetype == SERVICES:
+  if pagetype == SERVICES or pagetype == SERVICE_LIST:
     sv['currentItem'] = True
   else:
     sv['currentItem'] = False
@@ -2248,10 +2727,10 @@
   if model.getIsVirtualized() == True:
     vmadd = {}
     vmadd['Title'] = "Add a Virtual Service"
-    vmadd['cfg_type'] = "xenvmadd"
-    vmadd['absolute_url'] = url + "?pagetype=" + XENVM_ADD + "&clustername=" + cluname
+    vmadd['cfg_type'] = "vmadd"
+    vmadd['absolute_url'] = url + "?pagetype=" + VM_ADD + "&clustername=" + cluname
     vmadd['Description'] = "Add a Virtual Service to this cluster"
-    if pagetype == XENVM_ADD:
+    if pagetype == VM_ADD:
       vmadd['currentItem'] = True
     else:
       vmadd['currentItem'] = False
@@ -2261,17 +2740,16 @@
   svcfg['cfg_type'] = "servicecfg"
   svcfg['absolute_url'] = url + "?pagetype=" + SERVICE_CONFIG + "&clustername=" + cluname
   svcfg['Description'] = "Configure a Service for this cluster"
-  if pagetype == SERVICE_CONFIG or pagetype == SERVICE:
+  if pagetype == SERVICE_CONFIG or pagetype == SERVICE or pagetype == VM_CONFIG:
     svcfg['show_children'] = True
   else:
     svcfg['show_children'] = False
-  if pagetype == SERVICE_CONFIG:
+  if pagetype == SERVICE_CONFIG or pagetype == VM_CONFIG:
     svcfg['currentItem'] = True
   else:
     svcfg['currentItem'] = False
 
   services = model.getServices()
-  xenvms = model.getXENVMs()
   serviceable = list()
   for service in services:
     servicename = service.getName()
@@ -2294,19 +2772,20 @@
 
     serviceable.append(svc)
 
-  for xenvm in xenvms:
-    xenname = xenvm.getName()
+  vms = model.getVMs()
+  for vm in vms:
+    name = vm.getName()
     svc = {}
-    svc['Title'] = xenname
-    svc['cfg_type'] = "xenvm"
-    svc['absolute_url'] = url + "?pagetype=" + XENVM_CONFIG + "&servicename=" + xenname + "&clustername=" + cluname
+    svc['Title'] = name
+    svc['cfg_type'] = "vm"
+    svc['absolute_url'] = url + "?pagetype=" + VM_CONFIG + "&servicename=" + name + "&clustername=" + cluname
     svc['Description'] = "Configure this Virtual Service"
-    if pagetype == XENVM_CONFIG:
+    if pagetype == VM_CONFIG:
       try:
         xname = request['servicename']
       except KeyError, e:
         xname = ""
-      if xenname == xname:
+      if name == xname:
         svc['currentItem'] = True
       else:
         svc['currentItem'] = False
@@ -2716,7 +3195,7 @@
 		except Exception, e:
 			luci_log.debug_verbose('GRA4b: %s' % str(e))
 			cur_alias = None
-			
+
 		if (cur_name is not None and cluname != cur_name) and (cur_alias is not None and cluname != cur_alias):
 			try:
 				luci_log.debug('GRA5: %s reports it\'s in cluster %s:%s; we expect %s' \
@@ -2835,17 +3314,26 @@
 			% (clustername, cluster_path, str(e)))
 		return results
 
-	for node in nodelist:
+	if len(nodelist) < 1:
+		luci_log.debug_verbose('GCSDB0a: removing cluster %s because it has no nodes' % clustername)
 		try:
-			node_val = {}
-			node_val['type'] = 'node'
-			node_val['name'] = node[0]
-			node_val['clustered'] = '[unknown]'
-			node_val['online'] = '[unknown]'
-			node_val['error'] = True
-			results.append(node_val)
+			clusters_dir = self.restrictedTraverse(CLUSTER_FOLDER_PATH)
+			clusters_dir.manage_delObjects([clustername])
 		except Exception, e:
-			luci_log.debug_verbose('GCSDB1: %s' % str(e))
+			luci_log.debug_verbose('GCSDB0b: %s: %s' % (clustername, str(e)))
+	else:
+		for node in nodelist:
+			try:
+				node_val = {}
+				node_val['type'] = 'node'
+				node_val['name'] = node[0]
+				node_val['clustered'] = '[unknown]'
+				node_val['online'] = '[unknown]'
+				node_val['error'] = True
+				results.append(node_val)
+			except Exception, e:
+				luci_log.debug_verbose('GCSDB1: %s' % str(e))
+
 	return results
 
 def getClusterStatus(self, request, rc, cluname=None):
@@ -2922,6 +3410,10 @@
 			vals['name'] = node.getAttribute('name')
 			vals['nodename'] = node.getAttribute('nodename')
 			vals['running'] = node.getAttribute('running')
+			try:
+				vals['is_vm'] = node.getAttribute('vm').lower() == 'true'
+			except:
+				vals['is_vm'] = False
 			vals['failed'] = node.getAttribute('failed')
 			vals['autostart'] = node.getAttribute('autostart')
 			results.append(vals)
@@ -2939,6 +3431,7 @@
 		baseurl = '/luci/cluster/index_html'
 
 	try:
+		nodes = model.getNodes()
 		cluname = req['clustername']
 		if not cluname:
 			raise KeyError, 'is blank'
@@ -2954,14 +3447,49 @@
 		if item['type'] == "service":
 			itemmap = {}
 			itemmap['name'] = item['name']
+
+			cur_node = None
 			if item['running'] == "true":
+				cur_node = item['nodename']
 				itemmap['running'] = "true"
-				itemmap['nodename'] = item['nodename']
+				itemmap['nodename'] = cur_node
+				itemmap['disableurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + item['name'] + "&pagetype=" + SERVICE_STOP
+				itemmap['restarturl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + item['name'] + "&pagetype=" + SERVICE_RESTART
+			else:
+				itemmap['enableurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + item['name'] + "&pagetype=" + SERVICE_START
+
 			itemmap['autostart'] = item['autostart']
-			itemmap['cfgurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + SERVICE
-			itemmap['delurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + SERVICE_DELETE
 
-			svc = model.retrieveServiceByName(item['name'])
+			try:
+				svc = model.retrieveServiceByName(item['name'])
+				itemmap['cfgurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + SERVICE
+				itemmap['delurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + SERVICE_DELETE
+			except:
+				try:
+					svc = model.retrieveVMsByName(item['name'])
+					itemmap['is_vm'] = True
+					itemmap['cfgurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + VM_CONFIG 
+					itemmap['delurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + VM_CONFIG
+				except:
+					continue
+
+			starturls = list()
+			for node in nodes:
+				if node.getName() != cur_node:
+					starturl = {}
+					cur_nodename = node.getName()
+					starturl['nodename'] = cur_nodename
+					starturl['url'] = baseurl + '?' + 'clustername=' + cluname +'&servicename=' + item['name'] + '&pagetype=' + SERVICE_START + '&nodename=' + node.getName()
+					starturls.append(starturl)
+
+					if itemmap.has_key('is_vm') and itemmap['is_vm'] is True:
+						migrate_url = { 'nodename': cur_nodename }
+						migrate_url['migrate'] = True
+						migrate_url['url'] = baseurl + '?' + 'clustername=' + cluname +'&servicename=' + item['name'] + '&pagetype=' + SERVICE_MIGRATE + '&nodename=' + node.getName()
+						starturls.append(migrate_url)
+
+			itemmap['links'] = starturls
+
 			dom = svc.getAttribute("domain")
 			if dom is not None:
 				itemmap['faildom'] = dom
@@ -2972,6 +3500,9 @@
 	map['services'] = maplist
 	return map
 
+def get_fdom_names(model):
+	return map(lambda x: x.getName(), model.getFailoverDomains())
+
 def getServiceInfo(self, status, model, req):
 	#set up struct for service config page
 	hmap = {}
@@ -2985,6 +3516,11 @@
 		baseurl = '/luci/cluster/index_html'
 
 	try:
+		hmap['fdoms'] = get_fdom_names(model)
+	except:
+		hmap['fdoms'] = list()
+
+	try:
 		cluname = req['clustername']
 		if not cluname:
 			raise KeyError, 'is blank'
@@ -3007,48 +3543,83 @@
 		return hmap
 
 	for item in status:
-		if item['type'] == "service":
+		innermap = {}
+		if item['type'] == 'service':
 			if item['name'] == servicename:
 				hmap['name'] = servicename
-				starturls = list()
 				hmap['autostart'] = item['autostart']
-				if item['running'] == "true":
-					hmap['running'] = "true"
-					#In this case, determine where it can run...
-					innermap = {}
+
+				starturls = list()
+				if item['running'] == 'true':
+					hmap['running'] = 'true'
 					nodename = item['nodename']
-					innermap['current'] = "This service is currently running on %s" % nodename
+					innermap['current'] = 'This service is currently running on %s' % nodename
+
 					innermap['disableurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_STOP
 					innermap['restarturl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_RESTART
 					innermap['delurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_DELETE
 
+					#In this case, determine where it can run...
 					nodes = model.getNodes()
 					for node in nodes:
-						starturl = {}
 						if node.getName() != nodename:
-							starturl['nodename'] = node.getName()
+							starturl = {}
+							cur_nodename = node.getName()
+							starturl['nodename'] = cur_nodename
 							starturl['url'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_START + "&nodename=" + node.getName()
 							starturls.append(starturl)
+
+							if item.has_key('is_vm') and item['is_vm'] is True:
+								migrate_url = { 'nodename': cur_nodename }
+								migrate_url['url'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_MIGRATE + "&nodename=" + node.getName()
+								migrate_url['migrate'] = True
+								starturls.append(migrate_url)
 					innermap['links'] = starturls
 				else:
 					#Do not set ['running'] in this case...ZPT will detect it is missing
-					#In this case, determine where it can run...
-					innermap = {}
 					innermap['current'] = "This service is currently stopped"
 					innermap['enableurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_START
+					innermap['delurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_DELETE
+
 					nodes = model.getNodes()
 					starturls = list()
 					for node in nodes:
 						starturl = {}
-						starturl['nodename'] = node.getName()
+						cur_nodename = node.getName()
+
+						starturl['nodename'] = cur_nodename
 						starturl['url'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_START + "&nodename=" + node.getName()
 						starturls.append(starturl)
+
+						if item.has_key('is_vm') and item['is_vm'] is True:
+							migrate_url = { 'nodename': cur_nodename }
+							migrate_url['url'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_MIGRATE + "&nodename=" + node.getName()
+							migrate_url['migrate'] = True
+							starturls.append(migrate_url)
 					innermap['links'] = starturls
 				hmap['innermap'] = innermap
 
 	#Now build hashes for resources under service.
 	#first get service by name from model
 	svc = model.getService(servicename)
+	try:
+		hmap['domain'] = svc.getAttribute('domain')
+	except:
+		hmap['domain'] = None
+
+	try:
+		hmap['recovery'] = svc.getAttribute('recovery')
+	except:
+		hmap['recovery'] = None
+
+	try:
+		if int(svc.getAttribute('exclusive')):
+			hmap['exclusive'] = 'true' 
+		else:
+			hmap['exclusive'] = 'false'
+	except:
+		hmap['exclusive'] = 'false'
+
 	resource_list = list()
 	if svc is not None:
 		indent_ctr = 0
@@ -3072,9 +3643,11 @@
 	#Note: Final version needs all resource attrs
 	if child.isRefObject() == True:
 		rc_map['ref_object'] = True
+		rc_map['tag_name'] = child.getObj().TAG_NAME
 		rc_map['type'] = child.getObj().getResourceType()
 		rc_map['attrs'] = child.getObj().getAttributes()
 	else:
+		rc_map['tag_name'] = child.TAG_NAME
 		rc_map['type'] = child.getResourceType()
 		rc_map['attrs'] = child.getAttributes()
 
@@ -3133,19 +3706,22 @@
 
 	batch_number, result = startService(rc, svcname, nodename)
 	if batch_number is None or result is None:
-		luci_log.debug_verbose('startService3: SS(%s,%s,%s) call failed' \
+		luci_log.debug_verbose('serviceStart3: SS(%s,%s,%s) call failed' \
 			% (svcname, cluname, nodename))
 		return None
 
 	try:
-		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_START, "Starting service \'%s\'" % svcname)
+		status_msg = "Starting service \'%s\'" % svcname
+		if nodename:
+			status_msg += " on node \'%s\'" % nodename
+		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_START, status_msg)
 	except Exception, e:
-		luci_log.debug_verbose('startService4: error setting flags for service %s at node %s for cluster %s' % (svcname, nodename, cluname))
+		luci_log.debug_verbose('serviceStart4: error setting flags for service %s at node %s for cluster %s' % (svcname, nodename, cluname))
 
 	response = req.RESPONSE
 	response.redirect(req['URL'] + "?pagetype=" + SERVICE_LIST + "&clustername=" + cluname + '&busyfirst=true')
 
-def serviceRestart(self, rc, req):
+def serviceMigrate(self, rc, req):
 	svcname = None
 	try:
 		svcname = req['servicename']
@@ -3156,36 +3732,51 @@
 			pass
 
 	if svcname is None:
-		luci_log.debug_verbose('serviceRestart0: no service name')
+		luci_log.debug_verbose('serviceMigrate0: no service name')
 		return None
 
+	nodename = None
+	try:
+		nodename = req['nodename']
+	except:
+		try:
+			nodename = req.form['nodename']
+		except:
+			pass
+
+	if nodename is None:
+		luci_log.debug_verbose('serviceMigrate1: no target node name')
+		return None
+		
 	cluname = None
 	try:
 		cluname = req['clustername']
-	except:
+	except KeyError, e:
 		try:
 			cluname = req.form['clustername']
 		except:
 			pass
 
 	if cluname is None:
-		luci_log.debug_verbose('serviceRestart1: no cluster for %s' % svcname)
+		luci_log.debug_verbose('serviceMigrate2: no cluster name for svc %s' \
+			% svcname)
 		return None
 
-	batch_number, result = restartService(rc, svcname)
+	batch_number, result = migrateService(rc, svcname, nodename)
 	if batch_number is None or result is None:
-		luci_log.debug_verbose('serviceRestart2: %s failed' % svcname)
+		luci_log.debug_verbose('serviceMigrate3: SS(%s,%s,%s) call failed' \
+			% (svcname, cluname, nodename))
 		return None
-				
+
 	try:
-		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_RESTART, "Restarting service \'%s\'" % svcname)
+		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_START, "Migrating service \'%s\' to node \'%s\'" % (svcname, nodename))
 	except Exception, e:
-		luci_log.debug_verbose('serviceRestart3: error setting flags for service %s for cluster %s' % (svcname, cluname))
+		luci_log.debug_verbose('serviceMigrate4: error setting flags for service %s at node %s for cluster %s' % (svcname, nodename, cluname))
 
 	response = req.RESPONSE
 	response.redirect(req['URL'] + "?pagetype=" + SERVICE_LIST + "&clustername=" + cluname + '&busyfirst=true')
 
-def serviceStop(self, rc, req):
+def serviceRestart(self, rc, req):
 	svcname = None
 	try:
 		svcname = req['servicename']
@@ -3196,7 +3787,7 @@
 			pass
 
 	if svcname is None:
-		luci_log.debug_verbose('serviceStop0: no service name')
+		luci_log.debug_verbose('serviceRestart0: no service name')
 		return None
 
 	cluname = None
@@ -3209,23 +3800,96 @@
 			pass
 
 	if cluname is None:
-		luci_log.debug_verbose('serviceStop1: no cluster name for %s' % svcname)
+		luci_log.debug_verbose('serviceRestart1: no cluster for %s' % svcname)
 		return None
 
-	batch_number, result = stopService(rc, svcname)
+	batch_number, result = restartService(rc, svcname)
 	if batch_number is None or result is None:
-		luci_log.debug_verbose('serviceStop2: stop %s failed' % svcname)
+		luci_log.debug_verbose('serviceRestart2: %s failed' % svcname)
 		return None
 
 	try:
-		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_STOP, "Stopping service \'%s\'" % svcname)
+		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_RESTART, "Restarting service \'%s\'" % svcname)
 	except Exception, e:
-		luci_log.debug_verbose('serviceStop3: error setting flags for service %s for cluster %s' % (svcname, cluname))
+		luci_log.debug_verbose('serviceRestart3: error setting flags for service %s for cluster %s' % (svcname, cluname))
 
 	response = req.RESPONSE
 	response.redirect(req['URL'] + "?pagetype=" + SERVICE_LIST + "&clustername=" + cluname + '&busyfirst=true')
 
-def getFdomsInfo(self, model, request, clustatus):
+def serviceStop(self, rc, req):
+	svcname = None
+	try:
+		svcname = req['servicename']
+	except:
+		try:
+			svcname = req.form['servicename']
+		except:
+			pass
+
+	if svcname is None:
+		luci_log.debug_verbose('serviceStop0: no service name')
+		return None
+
+	cluname = None
+	try:
+		cluname = req['clustername']
+	except:
+		try:
+			cluname = req.form['clustername']
+		except:
+			pass
+
+	if cluname is None:
+		luci_log.debug_verbose('serviceStop1: no cluster name for %s' % svcname)
+		return None
+
+	batch_number, result = stopService(rc, svcname)
+	if batch_number is None or result is None:
+		luci_log.debug_verbose('serviceStop2: stop %s failed' % svcname)
+		return None
+
+	try:
+		set_node_flag(self, cluname, rc.hostname(), str(batch_number), SERVICE_STOP, "Stopping service \'%s\'" % svcname)
+	except Exception, e:
+		luci_log.debug_verbose('serviceStop3: error setting flags for service %s for cluster %s' % (svcname, cluname))
+
+	response = req.RESPONSE
+	response.redirect(req['URL'] + "?pagetype=" + SERVICE_LIST + "&clustername=" + cluname + '&busyfirst=true')
+
+def getFdomInfo(self, model, request):
+	fhash = {}
+	fhash['members'] = {}
+
+	try:
+		fdom = model.getFailoverDomainByName(request['fdomname'])
+	except Exception, e:
+		luci_log.debug_verbose('getFdomInfo0: %s' % str(e))
+		return fhash
+
+	fhash['name'] = fdom.getName()
+
+	ordered_attr = fdom.getAttribute('ordered')
+	if ordered_attr is not None and (ordered_attr == "true" or ordered_attr == "1"):
+		fhash['prioritized'] = '1'
+	else:
+		fhash['prioritized'] = '0'
+
+	restricted_attr = fdom.getAttribute('restricted')
+	if restricted_attr is not None and (restricted_attr == "true" or restricted_attr == "1"):
+		fhash['restricted'] = '1'
+	else:
+		fhash['restricted'] = '0'
+
+	nodes = fdom.getChildren()
+	for node in nodes:
+		try:
+			priority = node.getAttribute('priority')
+		except:
+			priority = '1'
+		fhash['members'][node.getName()] = { 'priority': priority }
+	return fhash
+
+def getFdomsInfo(self, model, request, clustatus):
   slist = list()
   nlist = list()
   for item in clustatus:
@@ -3241,7 +3905,7 @@
   for fdom in fdoms:
     fdom_map = {}
     fdom_map['name'] = fdom.getName()
-    fdom_map['cfgurl'] = baseurl + "?pagetype=" + FDOM_LIST + "&clustername=" + clustername
+    fdom_map['cfgurl'] = baseurl + "?pagetype=" + FDOM + "&clustername=" + clustername + '&fdomname=' + fdom.getName()
     ordered_attr = fdom.getAttribute('ordered')
     restricted_attr = fdom.getAttribute('restricted')
     if ordered_attr is not None and (ordered_attr == "true" or ordered_attr == "1"):
@@ -3326,6 +3990,7 @@
 	if not model:
 		return 'Unable to get the model object for %s' % cluname
 
+	redirect_page = NODES
 	if task == CLUSTER_STOP:
 		clusterStop(self, model)
 	elif task == CLUSTER_START:
@@ -3333,13 +3998,15 @@
 	elif task == CLUSTER_RESTART:
 		clusterRestart(self, model)
 	elif task == CLUSTER_DELETE:
-		clusterStop(self, model, delete=True)
+		ret = clusterDelete(self, model)
+		if ret is not None:
+			redirect_page = ret
 	else:
 		return 'An unknown cluster task was requested.'
 
 	response = request.RESPONSE
 	response.redirect('%s?pagetype=%s&clustername=%s&busyfirst=true' \
-		% (request['URL'], NODES, model.getClusterName()))
+		% (request['URL'], redirect_page, model.getClusterName()))
 
 def getClusterInfo(self, model, req):
   try:
@@ -3354,6 +4021,7 @@
         luci_log.debug_verbose('GCI0: unable to determine cluster name')
         return {}
 
+  clumap = {}
   if model is None:
     try:
       model = getModelForCluster(self, cluname)
@@ -3363,9 +4031,12 @@
     except Exception, e:
       luci_log.debug_verbose('GCI1: unable to get model for cluster %s: %s' % (cluname, str(e)))
       return {}
+  else:
+    totem = model.getTotemPtr()
+    if totem:
+      clumap['totem'] = totem.getAttributes()
 
   prop_baseurl = req['URL'] + '?' + PAGETYPE + '=' + CLUSTER_CONFIG + '&' + CLUNAME + '=' + cluname + '&'
-  clumap = {}
   basecluster_url = prop_baseurl + PROPERTIES_TAB + "=" + PROP_GENERAL_TAB
   #needed:
   clumap['basecluster_url'] = basecluster_url
@@ -3377,33 +4048,52 @@
   #-------------
   #new cluster params - if rhel5
   #-------------
-  #Fence Daemon Props
-  fencedaemon_url = prop_baseurl + PROPERTIES_TAB + "=" + PROP_FENCE_TAB
-  clumap['fencedaemon_url'] = fencedaemon_url
-  fdp = model.getFenceDaemonPtr()
-  pjd = fdp.getAttribute('post_join_delay')
-  if pjd is None:
-    pjd = "6"
-  pfd = fdp.getAttribute('post_fail_delay')
-  if pfd is None:
-    pfd = "0"
-  #post join delay
-  clumap['pjd'] = pjd
-  #post fail delay
-  clumap['pfd'] = pfd
-  #-------------
-  #if multicast
-  multicast_url = prop_baseurl + PROPERTIES_TAB + "=" + PROP_MCAST_TAB
-  clumap['multicast_url'] = multicast_url
-  #mcast addr
-  is_mcast = model.isMulticast()
-  #clumap['is_mcast'] = is_mcast
-  if is_mcast:
-    clumap['mcast_addr'] = model.getMcastAddr()
-    clumap['is_mcast'] = "True"
-  else:
-    clumap['is_mcast'] = "False"
-    clumap['mcast_addr'] = "1.2.3.4"
+
+  clumap['fence_xvmd'] = model.hasFenceXVM()
+  gulm_ptr = model.getGULMPtr()
+  if not gulm_ptr:
+    #Fence Daemon Props
+    fencedaemon_url = prop_baseurl + PROPERTIES_TAB + "=" + PROP_FENCE_TAB
+    clumap['fencedaemon_url'] = fencedaemon_url
+    fdp = model.getFenceDaemonPtr()
+    pjd = fdp.getAttribute('post_join_delay')
+    if pjd is None:
+      pjd = "6"
+    pfd = fdp.getAttribute('post_fail_delay')
+    if pfd is None:
+      pfd = "0"
+    #post join delay
+    clumap['pjd'] = pjd
+    #post fail delay
+    clumap['pfd'] = pfd
+
+    #-------------
+    #if multicast
+    multicast_url = prop_baseurl + PROPERTIES_TAB + "=" + PROP_MCAST_TAB
+    clumap['multicast_url'] = multicast_url
+    #mcast addr
+    is_mcast = model.isMulticast()
+    if is_mcast:
+      clumap['mcast_addr'] = model.getMcastAddr()
+      clumap['is_mcast'] = "True"
+    else:
+      clumap['is_mcast'] = "False"
+      clumap['mcast_addr'] = "1.2.3.4"
+    clumap['gulm'] = False
+  else:
+    #-------------
+    #GULM params (rhel4 only)
+    lockserv_list = list()
+    clunodes = model.getNodes()
+    gulm_lockservs = map(lambda x: x.getName(), gulm_ptr.getChildren())
+    lockserv_list = map(lambda x: (x, True), gulm_lockservs)
+    for node in clunodes:
+      n = node.getName()
+      if not n in gulm_lockservs:
+        lockserv_list.append((n, False))
+    clumap['gulm'] = True
+    clumap['gulm_url'] = prop_baseurl + PROPERTIES_TAB + '=' + PROP_GULM_TAB
+    clumap['gulm_lockservers'] = lockserv_list
 
   #-------------
   #quorum disk params
@@ -3448,27 +4138,21 @@
       clumap['label'] = label
 
     heuristic_kids = qdp.getChildren()
-    h_ctr = 0
+
     for kid in heuristic_kids:
       hmap = {}
-      hname = kid.getAttribute('name')
-      if hname is None:
-        hname = h_ctr
-        h_ctr = h_ctr + 1
       hprog = kid.getAttribute('program')
-      hscore = kid.getAttribute('score')
-      hinterval = kid.getAttribute('interval')
       if hprog is None:
         continue
-      if hname is not None:
-        hmap['hname'] = hname
-      else:
-        hmap['hname'] = ""
+
+      hscore = kid.getAttribute('score')
       hmap['hprog'] = hprog
       if hscore is not None:
         hmap['hscore'] = hscore
       else:
         hmap['hscore'] = ""
+
+      hinterval = kid.getAttribute('interval')
       if hinterval is not None:
         hmap['hinterval'] = hinterval
       else:
@@ -3528,7 +4212,12 @@
       svcname = svc['name']
       svc_dict['name'] = svcname
       svc_dict['srunning'] = svc['running']
-      svcurl = baseurl + "?" + PAGETYPE + "=" + SERVICE + "&" + CLUNAME + "=" + clustername + "&servicename=" + svcname
+
+      if svc.has_key('is_vm') and svc['is_vm'] is True:
+        target_page = VM_CONFIG
+      else:
+        target_page = SERVICE
+      svcurl = baseurl + "?" + PAGETYPE + "=" + target_page + "&" + CLUNAME + "=" + clustername + "&servicename=" + svcname
       svc_dict['servicename'] = svcname
       svc_dict['svcurl'] = svcurl
       svc_dict_list.append(svc_dict)
@@ -3621,7 +4310,7 @@
 			errors += 1
 			continue
 		if nodeJoin(self, rc, clustername, nodename_resolved) is None:
-			luci_log.debug_verbose('CStart1: nodeLeave %s' % nodename_resolved)
+			luci_log.debug_verbose('CStart1: nodeJoin %s' % nodename_resolved)
 			errors += 1
 
 	return errors
@@ -3644,18 +4333,18 @@
 			rc = RicciCommunicator(nodename_resolved)
 		except Exception, e:
 			luci_log.debug_verbose('CStop0: [%d] RC %s: %s' \
-				% (delete, nodename_resolved, str(e)))
+				% (delete is True, str(nodename_resolved), str(e)))
 			errors += 1
 			continue
 
 		if delete is True:
 			if nodeDelete(self, rc, model, clustername, nodename, nodename_resolved, delete_cluster=True) is None:
-				luci_log.debug_verbose('CStop1: nodeDelete failed')
+				luci_log.debug_verbose('CStop1: [1] nodeDelete failed')
 				errors += 1
 		else:
 			if nodeLeave(self, rc, clustername, nodename_resolved) is None:
-				luci_log.debug_verbose('CStop2: nodeLeave %s' \
-					% (delete, nodename_resolved))
+				luci_log.debug_verbose('CStop2: [0] nodeLeave %s' \
+					% (nodename_resolved))
 				errors += 1
 	return errors
 
@@ -3670,13 +4359,13 @@
 
 def clusterDelete(self, model):
 	num_errors = clusterStop(self, model, delete=True)
-	if num_errors < 1:
-		try:
-			clustername = model.getClusterName()
-		except Exception, e:
-			luci_log.debug_verbose('clusterDelete0: unable to get cluster name')
-			return None
+	try:
+		clustername = model.getClusterName()
+	except Exception, e:
+		luci_log.debug_verbose('clusterDelete0: unable to get cluster name')
+		return None
 
+	if num_errors < 1:
 		try:
 			delCluster(self, clustername)
 		except Exception, e:
@@ -3691,6 +4380,7 @@
 		except Exception, e:
 			luci_log.debug_verbose('clusterDelete2: %s %s' \
 				% (clustername, str(e)))
+		return CLUSTERLIST
 	else:
 		luci_log.debug_verbose('clusterDelete2: %s: %d errors' \
 			% (clustername, num_errors))
@@ -3882,8 +4572,6 @@
 				% (delete_target.getName(), str(e)))
 
 		try:
-			cp = model.getClusterPtr()
-			cp.incrementConfigVersion()
 			model.setModified(True)
 			str_buf = model.exportModelAsString()
 			if not str_buf:
@@ -4100,7 +4788,13 @@
   infohash['currentservices'] = svc_dict_list
 
   fdom_dict_list = list()
+  gulm_cluster = False
   if model:
+    gulm_cluster = model.getGULMPtr() is not None
+    try:
+      infohash['gulm_lockserver'] = model.isNodeLockserver(nodename)
+    except:
+      infohash['gulm_lockserver'] = False
     #next is faildoms
     fdoms = model.getFailoverDomainsForNode(nodename)
     for fdom in fdoms:
@@ -4109,6 +4803,8 @@
       fdomurl = baseurl + "?" + PAGETYPE + "=" + FDOM_CONFIG + "&" + CLUNAME + "=" + clustername + "&fdomname=" + fdom.getName()
       fdom_dict['fdomurl'] = fdomurl
       fdom_dict_list.append(fdom_dict)
+  else:
+    infohash['gulm_lockserver'] = False
 
   infohash['fdoms'] = fdom_dict_list
 
@@ -4132,8 +4828,11 @@
     if rc is not None:
       dlist = list()
       dlist.append("ccsd")
-      dlist.append("cman")
-      dlist.append("fenced")
+      if not gulm_cluster:
+        dlist.append("cman")
+        dlist.append("fenced")
+      else:
+        dlist.append("lock_gulmd")
       dlist.append("rgmanager")
       states = getDaemonStates(rc, dlist)
       infohash['d_states'] = states
@@ -4174,13 +4873,17 @@
         except:
           luci_log.debug_verbose('GNI0: unable to determine cluster name')
           return {}
-      
- 
+
   for item in nodelist:
     map = {}
     name = item['name']
     map['nodename'] = name
     try:
+      map['gulm_lockserver'] = model.isNodeLockserver(name)
+    except:
+      map['gulm_lockserver'] = False
+
+    try:
       baseurl = req['URL']
     except:
       baseurl = '/luci/cluster/index_html'
@@ -4285,7 +4988,7 @@
               clustername = model.getClusterName()
               node_hash = {}
               node_hash['nodename'] = node.getName().strip()
-              node_hash['nodeurl'] = baseurl + "?clustername=" + clustername + "&nodename=" + node.getName() + "&pagetype=" + NODE 
+              node_hash['nodeurl'] = baseurl + "?clustername=" + clustername + "&nodename=" + node.getName() + "&pagetype=" + NODE
               nodes_used.append(node_hash)
 
       map['nodesused'] = nodes_used
@@ -4299,7 +5002,7 @@
       return fd
 
   raise
-  
+
 def getFenceInfo(self, model, request):
   if not model:
     luci_log.debug_verbose('getFenceInfo00: model is None')
@@ -4342,12 +5045,12 @@
       luci_log.debug_verbose('getFenceInfo2: unable to extract nodename: %s' \
           % str(e))
       return {}
-    
+
   #Here we need to get fences for a node - just the first two levels
   #Each level has its own list of fence devs used in that level
   #For each fence dev, a list of instance structs is appended
   #In addition, for each level, a list of available but unused fence devs
-  #is returned. 
+  #is returned.
   try:
     node = model.retrieveNodeByName(nodename)
   except GeneralError, e:
@@ -4396,7 +5099,7 @@
             if kee == "name":
               continue #Don't duplicate name attr
             fencedev[kee] = kidattrs[kee]
-          #This fencedev struct is complete, and needs to be placed on the 
+          #This fencedev struct is complete, and needs to be placed on the
           #level1 Q. Because it is non-shared, we should set last_kid_fd
           #to none.
           last_kid_fd = None
@@ -4425,7 +5128,7 @@
               fencedev['unknown'] = True
               fencedev['prettyname'] = fd.getAgentType()
             fencedev['isShared'] = True
-            fencedev['cfgurl'] = baseurl + "?clustername=" + clustername + "&fencename=" + fd.getName().strip() + "&pagetype=" + FENCEDEV 
+            fencedev['cfgurl'] = baseurl + "?clustername=" + clustername + "&fencename=" + fd.getName().strip() + "&pagetype=" + FENCEDEV
             fencedev['id'] = str(major_num)
             major_num = major_num + 1
             inlist = list()
@@ -4441,7 +5144,7 @@
               if kee == "name":
                 continue
               instance_struct[kee] = kidattrs[kee]
-            inlist.append(instance_struct) 
+            inlist.append(instance_struct)
             level1.append(fencedev)
             last_kid_fd = fencedev
             continue
@@ -4503,7 +5206,7 @@
             if kee == "name":
               continue #Don't duplicate name attr
             fencedev[kee] = kidattrs[kee]
-          #This fencedev struct is complete, and needs to be placed on the 
+          #This fencedev struct is complete, and needs to be placed on the
           #level2 Q. Because it is non-shared, we should set last_kid_fd
           #to none.
           last_kid_fd = None
@@ -4532,7 +5235,7 @@
               fencedev['unknown'] = True
               fencedev['prettyname'] = fd.getAgentType()
             fencedev['isShared'] = True
-            fencedev['cfgurl'] = baseurl + "?clustername=" + clustername + "&fencename=" + fd.getName().strip() + "&pagetype=" + FENCEDEV 
+            fencedev['cfgurl'] = baseurl + "?clustername=" + clustername + "&fencename=" + fd.getName().strip() + "&pagetype=" + FENCEDEV
             fencedev['id'] = str(major_num)
             major_num = major_num + 1
             inlist = list()
@@ -4548,7 +5251,7 @@
               if kee == "name":
                 continue
               instance_struct[kee] = kidattrs[kee]
-            inlist.append(instance_struct) 
+            inlist.append(instance_struct)
             level2.append(fencedev)
             last_kid_fd = fencedev
             continue
@@ -4576,8 +5279,8 @@
         shared2.append(shared_struct)
     map['shared2'] = shared2
 
-  return map    
-      
+  return map
+
 def getFencesInfo(self, model, request):
   map = {}
   if not model:
@@ -4591,13 +5294,16 @@
 
   #Get list of fence devices
   fds = model.getFenceDevices()
-  nodes_used = list() #This section determines which nodes use the dev
   for fd in fds:
+    #This section determines which nodes use the dev
     #create fencedev hashmap
+    nodes_used = list()
+
     if fd.isShared() == True:
       fencedev = {}
       attr_hash = fd.getAttributes()
       kees = attr_hash.keys()
+
       for kee in kees:
         fencedev[kee] = attr_hash[kee] #copy attrs over
       try:
@@ -4605,6 +5311,7 @@
       except:
         fencedev['unknown'] = True
         fencedev['pretty_name'] = fd.getAgentType()
+
       fencedev['agent'] = fd.getAgentType()
       #Add config url for this fencedev
       fencedev['cfgurl'] = baseurl + "?clustername=" + clustername + "&fencename=" + fd.getName().strip() + "&pagetype=" + FENCEDEV
@@ -4625,7 +5332,7 @@
                 continue
               node_hash = {}
               node_hash['nodename'] = node.getName().strip()
-              node_hash['nodeurl'] = baseurl + "?clustername=" + clustername + "&nodename=" + node.getName() + "&pagetype=" + NODE 
+              node_hash['nodeurl'] = baseurl + "?clustername=" + clustername + "&nodename=" + node.getName() + "&pagetype=" + NODE
               nodes_used.append(node_hash)
 
       fencedev['nodesused'] = nodes_used
@@ -4634,7 +5341,6 @@
   map['fencedevs'] = fencedevs
   return map
 
-    
 def getLogsForNode(self, request):
 	try:
 		nodename = request['nodename']
@@ -4693,70 +5399,43 @@
 
 	return getNodeLogs(rc)
 
-def processXenVM(self, req):
-  model = req.SESSION.get('model')
-  isNew = False
+def getVMInfo(self, model, request):
+  map = {}
+  baseurl = request['URL']
+  clustername = request['clustername']
+  svcname = None
+
   try:
-    xenvmname = req['servicename']
+    svcname = request['servicename']
   except KeyError, e:
-    isNew = True
-  
-  if isNew == True:
-    xvm = Vm()
-    xvm.addAttribute("name", req.form['xenvmname'])
-    xvm.addAttribute("path", req.form['xenvmpath'])
-    rmptr = model.getResourceManagerPtr()
-    rmptr.addChild(xvm)
-  else:
-    xvm = model.retrieveXenVMsByName(self, xenvmname)
-    xvm.addAttribute("name", req.form['xenvmname'])
-    xvm.addAttribute("path", req.form['xenvmpath'])
+    svcname = None
+  urlstring = baseurl + "?" + clustername + "&pagetype=29"
+  if svcname != None:
+    urlstring = urlstring + "&servicename=" + svcname
 
-  try:
-    cp = model.getClusterPtr()
-    cp.incrementConfigVersion()
-    model.setModified(True)
-    stringbuf = model.exportModelAsString()
-    if not stringbuf:
-      raise Exception, 'model is blank'
-  except Exception, e:
-    luci_log.debug_verbose('exportModelAsString error: %s' % str(e))
-    return None
+  map['formurl'] = urlstring
 
   try:
-    clustername = model.getClusterName()
-    if not clustername:
-      raise Exception, 'cluster name from model.getClusterName() is blank'
-  except Exception, e:
-    luci_log.debug_verbose('error: getClusterName: %s' % str(e))
-    return None
-
-  rc = getRicciAgent(self, clustername)
-  if not rc:
-    luci_log.debug_verbose('Unable to find a ricci agent for the %s cluster' % clustername)
-    return None
-
-  setClusterConf(rc, stringbuf)
-
-def getXenVMInfo(self, model, request):
-	try:
-		xenvmname = request['servicename']
-	except:
-		try:
-			xenvmname = request.form['servicename']
-		except:
-			luci_log.debug_verbose('servicename is missing from request')
-			return {}
+    vmname = request['servicename']
+  except:
+    try:
+      vmname = request.form['servicename']
+    except:
+      luci_log.debug_verbose('servicename is missing from request')
+      return map
 
-	try:
-		xenvm = model.retrieveXenVMsByName(xenvmname)
-	except:
-		luci_log.debug('An error occurred while attempting to get VM %s' \
-			% xenvmname)
-		return {}
+  try:
+    vm = model.retrieveVMsByName(vmname)
+  except:
+    luci_log.debug('An error occurred while attempting to get VM %s' \
+      % vmname)
+    return map
 
-	map = xenvm.getAttributes()
-	return map
+  attrs = vm.getAttributes()
+  keys = attrs.keys()
+  for key in keys:
+    map[key] = attrs[key]
+  return map
 
 def isClusterBusy(self, req):
   items = None
@@ -4829,7 +5508,7 @@
       node_report = {}
       node_report['isnodecreation'] = True
       node_report['iserror'] = False  #Default value
-      node_report['desc'] = item[1].getProperty(FLAG_DESC) 
+      node_report['desc'] = item[1].getProperty(FLAG_DESC)
       batch_xml = None
       ricci = item[0].split("____") #This removes the 'flag' suffix
 
@@ -4885,18 +5564,18 @@
       if batch_xml is None:  #The job is done and gone from queue
         if redirect_message == False: #We have not displayed this message yet
           node_report['desc'] = REDIRECT_MSG
-          node_report['iserror'] = True 
+          node_report['iserror'] = True
           node_report['errormessage'] = ""
           nodereports.append(node_report)
           redirect_message = True
 
         luci_log.debug_verbose('ICB13: batch job is done -- deleting %s' % item[0])
-        clusterfolder.manage_delObjects(item[0])
+        clusterfolder.manage_delObjects([item[0]])
         continue
 
-
-
+      del_db_obj = False
       if creation_status < 0:  #an error was encountered
+        luci_log.debug_verbose('ICB13a: %s: CS %d for %s' % (cluname, creation_status, ricci[0]))
         if creation_status == RICCI_CONNECT_FAILURE:
           laststatus = item[1].getProperty(LAST_STATUS)
           if laststatus == INSTALL_TASK: #This means maybe node is rebooting
@@ -4926,14 +5605,17 @@
           node_report['iserror'] = True
           (err_code, err_msg) = extract_module_status(batch_xml, INSTALL_TASK)
           node_report['errormessage'] = CLUNODE_CREATE_ERRORS[INSTALL_TASK] + err_msg
+          del_db_obj = True
         elif creation_status == -(DISABLE_SVC_TASK):
           node_report['iserror'] = True
           (err_code, err_msg) = extract_module_status(batch_xml, DISABLE_SVC_TASK)
           node_report['errormessage'] = CLUNODE_CREATE_ERRORS[DISABLE_SVC_TASK] + err_msg
+          del_db_obj = True
         elif creation_status == -(REBOOT_TASK):
           node_report['iserror'] = True
           (err_code, err_msg) = extract_module_status(batch_xml, REBOOT_TASK)
           node_report['errormessage'] = CLUNODE_CREATE_ERRORS[REBOOT_TASK] + err_msg
+          del_db_obj = True
         elif creation_status == -(SEND_CONF):
           node_report['iserror'] = True
           (err_code, err_msg) = extract_module_status(batch_xml, SEND_CONF)
@@ -4947,11 +5629,15 @@
           (err_code, err_msg) = extract_module_status(batch_xml, START_NODE)
           node_report['errormessage'] = CLUNODE_CREATE_ERRORS[START_NODE]
         else:
+          del_db_obj = True
           node_report['iserror'] = True
           node_report['errormessage'] = CLUNODE_CREATE_ERRORS[0]
 
         try:
-          clusterfolder.manage_delObjects(item[0])
+          if del_db_obj is True:
+            luci_log.debug_verbose('ICB13a: %s node creation failed for %s: %d: deleting DB entry' % (cluname, ricci[0], creation_status))
+            clusterfolder.manage_delObjects([ricci[0]])
+          clusterfolder.manage_delObjects([item[0]])
         except Exception, e:
           luci_log.debug_verbose('ICB14: delObjects: %s: %s' \
             % (item[0], str(e)))
@@ -4965,7 +5651,7 @@
           node_report['statusindex'] = creation_status
           nodereports.append(node_report)
           try:
-              clusterfolder.manage_delObjects(item[0])
+              clusterfolder.manage_delObjects([item[0]])
           except Exception, e:
               luci_log.info('ICB15: Unable to delete %s: %s' % (item[0], str(e)))
           continue
@@ -4984,7 +5670,7 @@
             luci_log.debug_verbose('ICB16: last_status err: %s %d: %s' \
               % (item[0], creation_status, str(e)))
           continue
-          
+
     else:
       node_report = {}
       node_report['isnodecreation'] = False
@@ -5016,7 +5702,7 @@
           node_report['desc'] = flag_msg + flag_desc + REDIRECT_MSG
         nodereports.append(node_report)
         try:
-            clusterfolder.manage_delObjects(item[0])
+            clusterfolder.manage_delObjects([item[0]])
         except Exception, e:
             luci_log.info('ICB16: Unable to delete %s: %s' % (item[0], str(e)))
       else:
@@ -5084,6 +5770,7 @@
 		itemmap['name'] = item.getName()
 		itemmap['attrs'] = item.attr_hash
 		itemmap['type'] = item.resource_type
+		itemmap['tag_name'] = item.TAG_NAME
 		itemmap['cfgurl'] = baseurl + "?" + "clustername=" + cluname + "&resourcename=" + item.getName() + "&pagetype=" + RESOURCE_CONFIG
 		itemmap['url'] = baseurl + "?" + "clustername=" + cluname + "&resourcename=" + item.getName() + "&pagetype=" + RESOURCE
 		itemmap['delurl'] = baseurl + "?" + "clustername=" + cluname + "&resourcename=" + item.getName() + "&pagetype=" + RESOURCE_REMOVE
@@ -5199,8 +5886,6 @@
 		return (False, {'errors': [ '%s: error removing service %s.' % (errstr, name) ]})
 
 	try:
-		cp = model.getClusterPtr()
-		cp.incrementConfigVersion()
 		model.setModified(True)
 		conf = model.exportModelAsString()
 		if not conf:
@@ -5281,8 +5966,6 @@
 		return errstr + ': the specified resource was not found.'
 
 	try:
-		cp = model.getClusterPtr()
-		cp.incrementConfigVersion()
 		model.setModified(True)
 		conf = model.exportModelAsString()
 		if not conf:
@@ -5298,7 +5981,7 @@
 		return errstr
 
 	try:
-		set_node_flag(self, clustername, ragent, str(batch_number), RESOURCE_REMOVE, "Removing Resource \'%s\'" % request['resourcename'])
+		set_node_flag(self, clustername, ragent, str(batch_number), RESOURCE_REMOVE, "Removing resource \'%s\'" % request['resourcename'])
 	except Exception, e:
 		luci_log.debug_verbose('delResource7: failed to set flags: %s' % str(e))
 
@@ -5306,590 +5989,1280 @@
 	response.redirect(request['URL'] + "?pagetype=" + RESOURCES + "&clustername=" + clustername + '&busyfirst=true')
 
 def addIp(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addIp error: form is missing')
+		luci_log.debug_verbose('addIp0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addIp error: model is missing')
+		luci_log.debug_verbose('addIp1: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank.'
+
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No IP resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addIp error: %s' % str(e))
-			return None
+			errors.append('No original name was found for this IP resource.')
 	else:
 		try:
 			res = Ip()
 			if not res:
-				raise Exception, 'apply(Ip) is None'
+				raise Exception, 'res is None'
 		except Exception, e:
-			luci_log.debug_verbose('addIp error: %s' % str(e))
-			return None
+			errors.append('An error occurred while creating an IP resource.')
+			luci_log.debug_verbose('addIp3: %s' % str(e))
 
 	if not res:
-		luci_log.debug_verbose('addIp error: res is none')
-		return None
+		return [None, None, errors]
 
-	errors = list()
 	try:
 		addr = form['ip_address'].strip()
 		if not addr:
 			raise KeyError, 'ip_address is blank'
 		# XXX: validate IP addr
-		res.attr_hash['address'] = addr
+		res.addAttribute('address', addr)
 	except KeyError, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addIp error: %s' % err)
+		luci_log.debug_verbose('addIp4: %s' % err)
 
 	if 'monitorLink' in form:
-		res.attr_hash['monitor_link'] = '1'
+		res.addAttribute('monitor_link', '1')
 	else:
-		res.attr_hash['monitor_link'] = '0'
+		res.addAttribute('monitor_link', '0')
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
 def addFs(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addFs error: form is missing')
+		luci_log.debug_verbose('addFs0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addFs error: model is missing')
+		luci_log.debug_verbose('addFs1: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank.'
+
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No filesystem resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addFs error: %s' % str(e))
-			return None
+			errors.append('No original name was found for this filesystem resource.')
+			luci_log.debug_verbose('addFs3: %s' % str(e))
 	else:
 		try:
 			res = Fs()
 			if not res:
-				raise Exception, 'apply(Fs) is None'
+				raise Exception, 'res is None'
 		except Exception, e:
-			luci_log.debug_verbose('addFs error: %s' % str(e))
-			return None
+			errors.append('An error occurred while creating a filesystem resource.')
+			luci_log.debug_verbose('addFs4: %s' % str(e))
 
 	if not res:
-		luci_log.debug_verbose('addFs error: fs obj was not created')
-		return None
+		return [None, None, errors]
 
 	# XXX: sanity check these fields
-	errors = list()
 	try:
 		name = form['resourceName'].strip()
-		res.attr_hash['name'] = name
+		if not name:
+			raise Exception, 'No name was given for this filesystem resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addFs error: %s' % err)
+		luci_log.debug_verbose('addFs5: %s' % err)
 
 	try:
 		mountpoint = form['mountpoint'].strip()
-		res.attr_hash['mountpoint'] = mountpoint
+		if not mountpoint:
+			raise Exception, 'No mount point was given for this filesystem resource.'
+		res.addAttribute('mountpoint', mountpoint)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addFs error: %s' % err)
+		luci_log.debug_verbose('addFs6: %s' % err)
 
 	try:
 		device = form['device'].strip()
-		res.attr_hash['device'] = device
+		if not device:
+			raise Exception, 'No device was given for this filesystem resource.'
+		res.addAttribute('device', device)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addFs error: %s' % err)
+		luci_log.debug_verbose('addFs7: %s' % err)
 
 	try:
 		options = form['options'].strip()
-		res.attr_hash['options'] = options
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('options')
+		except:
+			pass
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addFs error: %s' % err)
+		luci_log.debug_verbose('addFs8: %s' % err)
 
 	try:
 		fstype = form['fstype'].strip()
-		res.attr_hash['fstype'] = fstype
+		if not fstype:
+			raise Exception, 'No filesystem type was given for this filesystem resource.'
+		res.addAttribute('fstype', fstype)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addFs error: %s' % err)
+		luci_log.debug_verbose('addFs9: %s' % err)
 
 	try:
 		fsid = form['fsid'].strip()
-		res.attr_hash['fsid'] = fsid
+		if not fsid:
+			raise Exception, 'No filesystem ID was given for this filesystem resource.'
+		fsid_int = int(fsid)
+		if not fsid_is_unique(model, fsid_int):
+			raise Exception, 'The filesystem ID provided is not unique.'
 	except Exception, e:
-		err = str(e)
-		errors.append(err)
-		luci_log.debug_verbose('addFs error: %s' % err)
+		fsid = str(generate_fsid(model, name))
+	res.addAttribute('fsid', fsid)
 
 	if form.has_key('forceunmount'):
-		res.attr_hash['force_unmount'] = '1'
+		res.addAttribute('force_unmount', '1')
 	else:
-		res.attr_hash['force_unmount'] = '0'
+		res.addAttribute('force_unmount', '0')
 
 	if form.has_key('selffence'):
-		res.attr_hash['self_fence'] = '1'
+		res.addAttribute('self_fence', '1')
 	else:
-		res.attr_hash['self_fence'] = '0'
+		res.addAttribute('self_fence', '0')
 
 	if form.has_key('checkfs'):
-		res.attr_hash['force_fsck'] = '1'
+		res.addAttribute('force_fsck', '1')
 	else:
-		res.attr_hash['force_fsck'] = '0'
+		res.addAttribute('force_fsck', '0')
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
 def addGfs(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addGfs error: form is missing')
+		luci_log.debug_verbose('addGfs0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addGfs error: model is missing')
+		luci_log.debug_verbose('addGfs1: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
-			if not res:
-				luci_log.debug('resource %s was not found for editing' % oldname)
-				return None
+				raise Exception, 'oldname is blank'
+
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No filesystem resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug('resource %s was not found for editing: %s' \
-				% (oldname, str(e)))
-			return None
+			errors.append('No original name was found for this cluster filesystem resource.')
+			luci_log.debug_verbose('addGfs2: %s' % str(e))
 	else:
 		try:
 			res = Clusterfs()
 			if not res:
-				raise Exception, 'apply(Clusterfs) is None'
+				raise Exception, 'res is None'
 		except Exception, e:
-			luci_log.debug('addGfs error: %s' % str(e))
-			return None
-		except:
-			luci_log.debug('addGfs error')
-			return None
+			errors.append('An error occurred while creating a cluster filesystem resource.')
+			luci_log.debug('addGfs3: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
 
 	# XXX: sanity check these fields
-	errors = list()
 	try:
 		name = form['resourceName'].strip()
 		if not name:
-			raise KeyError, 'resourceName is blank'
-		res.attr_hash['name'] = name
+			raise Exception, 'No name was given for this cluster filesystem resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addGfs error: %s' % err)
+		luci_log.debug_verbose('addGfs4: %s' % err)
 
 	try:
 		mountpoint = form['mountpoint'].strip()
-		res.attr_hash['mountpoint'] = mountpoint
+		if not mountpoint:
+			raise Exception, 'No mount point was given for this cluster filesystem resource.'
+		res.addAttribute('mountpoint', mountpoint)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addGfs error: %s' % err)
+		luci_log.debug_verbose('addGfs5: %s' % err)
 
 	try:
 		device = form['device'].strip()
-		res.attr_hash['device'] = device
+		if not device:
+			raise Exception, 'No device was given for this cluster filesystem resource.'
+		res.addAttribute('device', device)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addGfs error: %s' % err)
+		luci_log.debug_verbose('addGfs6: %s' % err)
 
 	try:
 		options = form['options'].strip()
-		res.attr_hash['options'] = options
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('options')
+		except:
+			pass
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addGfs error: %s' % err)
+		luci_log.debug_verbose('addGfs7: %s' % err)
 
 	try:
 		fsid = form['fsid'].strip()
-		res.attr_hash['fsid'] = fsid
+		if not fsid:
+			raise Exception, 'No filesystem ID was given for this cluster filesystem resource.'
+		fsid_int = int(fsid)
+		if not fsid_is_unique(model, fsid_int):
+			raise Exception, 'The filesystem ID provided is not unique.'
 	except Exception, e:
-		err = str(e)
-		errors.append(err)
-		luci_log.debug_verbose('addGfs error: %s' % err)
+		fsid = str(generate_fsid(model, name))
+	res.addAttribute('fsid', fsid)
 
 	if form.has_key('forceunmount'):
-		res.attr_hash['force_unmount'] = '1'
+		res.addAttribute('force_unmount', '1')
 	else:
-		res.attr_hash['force_unmount'] = '0'
+		res.addAttribute('force_unmount', '0')
 
 	if len(errors) > 1:
 		return [None, None, errors]
+
 	return [res, model, None]
 
 def addNfsm(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addNfsm error: form is missing')
+		luci_log.debug_verbose('addNfsm0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addNfsm error: model is missing')
+		luci_log.debug_verbose('addNfsm1: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank'
+
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No NFS mount resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addNfsm error: %s' % str(e))
-			return None
+			errors.append('No original name was found for this NFS mount resource.')
+			luci_log.debug_verbose('addNfsm2: %s' % str(e))
 	else:
 		try:
 			res = Netfs()
+			if not res:
+				raise Exception, 'res is None'
 		except Exception, e:
-			luci_log.debug_verbose('addNfsm error: %s' % str(e))
-			return None
+			errors.append('An error occurred while creating a NFS mount resource.')
+			luci_log.debug_verbose('addNfsm3: %s' % str(e))
 
 	if not res:
-		return None
+		return [None, None, errors]
 
 	# XXX: sanity check these fields
-	errors = list()
 	try:
 		name = form['resourceName'].strip()
 		if not name:
-			raise KeyError, 'resourceName is blank'
-		res.attr_hash['name'] = name
+			raise Exception, 'No name was given for this NFS mount resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsm error: %s' % err)
+		luci_log.debug_verbose('addNfsm4: %s' % err)
 
 	try:
 		mountpoint = form['mountpoint'].strip()
-		res.attr_hash['mountpoint'] = mountpoint
+		if not mountpoint:
+			raise Exception, 'No mount point was given for NFS mount resource.'
+		res.addAttribute('mountpoint', mountpoint)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsm error: %s' % err)
-		
+		luci_log.debug_verbose('addNfsm5: %s' % err)
+
 	try:
 		host = form['host'].strip()
-		res.attr_hash['host'] = host
+		if not host:
+			raise Exception, 'No host server was given for this NFS mount resource.'
+		res.addAttribute('host', host)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsm error: %s' % err)
+		luci_log.debug_verbose('addNfsm6 error: %s' % err)
 
 	try:
 		options = form['options'].strip()
-		res.attr_hash['options'] = options
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('options')
+		except:
+			pass
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsm error: %s' % err)
+		luci_log.debug_verbose('addNfsm7: %s' % err)
 
 	try:
 		exportpath = form['exportpath'].strip()
-		res.attr_hash['exportpath'] = exportpath 
+		if not exportpath:
+			raise Exception, 'No export path was given for this NFS mount resource.'
+		res.addAttribute('exportpath', exportpath)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsm error: %s' % err)
+		luci_log.debug_verbose('addNfsm8: %s' % err)
 
 	try:
 		nfstype = form['nfstype'].strip().lower()
 		if nfstype != 'nfs' and nfstype != 'nfs4':
-			raise KeyError, 'invalid nfs type: %s' % nfstype
-		res.attr_hash['nfstype'] = nfstype
+			raise Exception, 'An invalid NFS version \"%s\" was given.' % nfstype
+		res.addAttribute('nfstype', nfstype)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsm error: %s' % err)
+		luci_log.debug_verbose('addNfsm9: %s' % err)
 
 	if form.has_key('forceunmount'):
-		res.attr_hash['force_unmount'] = '1'
+		res.addAttribute('force_unmount', '1')
 	else:
-		res.attr_hash['force_unmount'] = '0'
+		res.addAttribute('force_unmount', '0')
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
 def addNfsc(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addNfsc error: form is missing')
+		luci_log.debug_verbose('addNfsc0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addNfsc error: model is missing')
+		luci_log.debug_verbose('addNfsc1: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No NFS client resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addNfsc error: %s' % str(e))
-			return None
+			errors.append('No original name was found for this NFS client resource.')
+			luci_log.debug_verbose('addNfsc2: %s' % str(e))
 	else:
 		try:
 			res = NFSClient()
-		except:
-			luci_log.debug_verbose('addNfsc error: %s' % str(e))
-			return None
+			if not res:
+				raise Exception, 'res is None'
+		except Exception, e:
+			errors.append('An error occurred while creating a NFS client resource.')
+			luci_log.debug_verbose('addNfsc3: %s' % str(e))
 
 	if not res:
-		luci_log.debug_verbose('addNfsc error: res is none')
-		return None
+		return [None, None, errors]
 
-	errors = list()
+	# XXX: sanity check these fields
 	try:
 		name = form['resourceName'].strip()
 		if not name:
-			raise KeyError, 'resourceName is blank'
-		res.attr_hash['name'] = name
+			raise Exception, 'No name was given for this NFS client resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsc error: %s' % err)
+		luci_log.debug_verbose('addNfsc4: %s' % err)
 
 	try:
 		target = form['target'].strip()
-		res.attr_hash['target'] = target 
+		if not target:
+			raise Exception, 'No target was given for NFS client resource.'
+		res.addAttribute('target', target)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsc error: %s' % err)
+		luci_log.debug_verbose('addNfsc5: %s' % err)
 
 	try:
 		options = form['options'].strip()
-		res.attr_hash['options'] = options
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('options')
+		except:
+			pass
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsc error: %s' % err)
+		luci_log.debug_verbose('addNfsc6: %s' % err)
+
+	if form.has_key('allow_recover'):
+		res.addAttribute('allow_recover', '1')
+	else:
+		res.addAttribute('allow_recover', '0')
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
 def addNfsx(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addNfsx error: model is missing')
+		luci_log.debug_verbose('addNfsx0: model is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addNfsx error: model is missing')
+		luci_log.debug_verbose('addNfsx0: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No NFS export resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addNfsx error: %s', str(e))
-			return None
+			errors.append('No original name was found for this NFS export resource.')
+			luci_log.debug_verbose('addNfsx2: %s', str(e))
 	else:
 		try:
 			res = NFSExport()
-		except:
-			luci_log.debug_verbose('addNfsx error: %s', str(e))
-			return None
+			if not res:
+				raise Exception, 'res is None'
+		except Exception, e:
+			errors.append('An error occurred while creating a NFS clientresource.')
+			luci_log.debug_verbose('addNfsx3: %s', str(e))
 
 	if not res:
-		luci_log.debug_verbose('addNfsx error: res is None')
-		return None
+		return [None, None, errors]
 
-	errors = list()
 	try:
 		name = form['resourceName'].strip()
 		if not name:
-			raise KeyError, 'resourceName is blank'
-		res.attr_hash['name'] = name
+			raise Exception, 'No name was given for this NFS export resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addNfsx error: %s', err)
+		luci_log.debug_verbose('addNfsx4: %s' % err)
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
 def addScr(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addScr error: form is missing')
+		luci_log.debug_verbose('addScr0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addScr error: model is missing')
+		luci_log.debug_verbose('addScr1: model is missing')
 		return None
 
+	res = None
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No script resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addScr error: %s' % str(e))
-			return None
+			errors.append('No original name was found for this script resource.')
+			luci_log.debug_verbose('addScr2: %s' % str(e))
 	else:
 		try:
 			res = Script()
+			if not res:
+				raise Exception, 'res is None'
 		except Exception, e:
-			luci_log.debug_verbose('addScr error: %s' % str(e))
-			return None
+			errors.append('An error occurred while creating a script resource.')
+			luci_log.debug_verbose('addScr3: %s' % str(e))
 
 	if not res:
-		luci_log.debug_verbose('addScr error: res is None')
-		return None
+		return [None, None, errors]
 
-	errors = list()
 	try:
 		name = form['resourceName'].strip()
 		if not name:
-			raise KeyError, 'resourceName is blank'
-		res.attr_hash['name'] = name
+			raise Exception, 'No name was given for this script resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addScr error: %s' % err)
+		luci_log.debug_verbose('addScr4: %s' % err)
 
 	try:
 		path = form['file'].strip()
 		if not path:
-			raise KeyError, 'file path is blank'
-		res.attr_hash['file'] = path
+			raise Exception, 'No path to a script file was given for this script resource.'
+		res.addAttribute('file', path)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addScr error: %s' % err)
+		luci_log.debug_verbose('addScr5: %s' % err)
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
 def addSmb(request, form=None):
+	errors = list()
+
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addSmb error: form is missing')
+		luci_log.debug_verbose('addSmb0: form is missing')
 		return None
 
 	model = request.SESSION.get('model')
 	if not model:
-		luci_log.debug_verbose('addSmb error: model is missing')
+		luci_log.debug_verbose('addSmb1: model is missing')
 		return None
 
 	if form.has_key('edit'):
 		try:
 			oldname = form['oldname'].strip()
 			if not oldname:
-				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(model, oldname)
+				raise Exception, 'oldname is blank'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e1:
+				errors.append('No Samba resource named \"%s\" exists.' % oldname)
 		except Exception, e:
-			luci_log.debug_verbose('addSmb error: %s' % str(e))
-			return None
+			errors.append('No original name was found for this Samba resource.')
+			luci_log.debug_verbose('addSmb2: %s' % str(e))
 	else:
 		try:
 			res = Samba()
+			if not res:
+				raise Exception, 'res is None'
 		except Exception, e:
-			luci_log.debug_verbose('addSmb error: %s' % str(e))
-			return None
+			errors.append('An error occurred while creating a Samba resource.')
+			luci_log.debug_verbose('addSmb3: %s' % str(e))
 
 	if not res:
-		luci_log.debug_verbose('addSmb error: res is None')
-		return None
+		return [None, None, errors]
 
-	errors = list()
 	try:
 		name = form['resourceName'].strip()
 		if not name:
-			raise KeyError, 'resourceName is blank'
-		res.attr_hash['name'] = name
+			raise Exception, 'No name was given for this Samba resource.'
+		res.addAttribute('name', name)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addSmb error: %s' % err)
+		luci_log.debug_verbose('addSmb4: %s' % err)
 
 	try:
 		workgroup = form['workgroup'].strip()
-		res.attr_hash['workgroup'] = workgroup
+		if not workgroup:
+			raise Exception, 'No workgroup was given for this Samba resource.'
+		res.addAttribute('workgroup', workgroup)
 	except Exception, e:
 		err = str(e)
 		errors.append(err)
-		luci_log.debug_verbose('addSmb error: %s' % err)
+		luci_log.debug_verbose('addSmb5: %s' % err)
 
 	if len(errors) > 1:
 		return [None, None, errors]
 	return [res, model, None]
 
-resourceAddHandler = {
-	'ip': addIp,
-	'fs': addFs,
-	'gfs': addGfs,
-	'nfsm': addNfsm,
-	'nfsx': addNfsx,
-	'nfsc': addNfsc,
-	'scr': addScr,
-	'smb': addSmb
-}
+def addApache(request, form=None):
+	errors = list()
 
-def resolveClusterChanges(self, clusterName, model):
-	try:
+	if form is None:
+		form = request.form
+
+	if not form:
+		luci_log.debug_verbose('addApache0: form is missing')
+		return None
+
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addApache1: model is missing')
+		return None
+
+	res = None
+	if form.has_key('edit'):
+		try:
+			oldname = form['oldname'].strip()
+			if not oldname:
+				raise Exception, 'oldname is blank.'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e:
+				errors.append('No Apache resource named \"%s\" exists.' % oldname)
+		except Exception, e:
+			errors.append('No original name was found for this Apache resource.')
+			luci_log.debug_verbose('addApache2: %s' % str(e))
+	else:
+		try:
+			res = Apache()
+			if not res:
+				raise Exception, 'could not create Apache object'
+		except Exception, e:
+			errors.append('An error occurred while creating an Apache resource.')
+			luci_log.debug_verbose('addApache3: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
+
+	try:
+		name = form['resourceName'].strip()
+		if not name:
+			raise Exception, 'No name was given for this Apache resource.'
+		res.addAttribute('name', name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addApache4: %s' % err)
+
+	try:
+		server_root = form['server_root'].strip()
+		if not server_root:
+			raise KeyError, 'No server root was given for this Apache resource.'
+		res.addAttribute('server_root', server_root)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addApache5: %s' % err)
+
+	try:
+		config_file = form['config_file'].strip()
+		if not server_root:
+			raise KeyError, 'No path to the Apache configuration file was given.'
+		res.addAttribute('config_file', config_file)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addApache6: %s' % err)
+
+	try:
+		options = form['httpd_options'].strip()
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('httpd_options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('httpd_options')
+		except:
+			pass
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addApache7: %s' % err)
+
+	try:
+		shutdown_wait = int(form['shutdown_wait'].strip())
+		res.addAttribute('shutdown_wait', str(shutdown_wait))
+	except KeyError, e:
+		res.addAttribute('shutdown_wait', '0')
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addApache7: %s' % err)
+
+	if len(errors) > 1:
+		return [None, None, errors]
+	return [res, model, None]
+
+def addMySQL(request, form=None):
+	errors = list()
+
+	if form is None:
+		form = request.form
+
+	if not form:
+		luci_log.debug_verbose('addMySQL0: form is missing')
+		return None
+
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addMySQL1: model is missing')
+		return None
+
+	res = None
+	if form.has_key('edit'):
+		try:
+			oldname = form['oldname'].strip()
+			if not oldname:
+				raise Exception, 'oldname is blank.'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e:
+				errors.append('No MySQL resource named \"%s\" exists.' % oldname)
+		except Exception, e:
+			errors.append('No original name was found for this MySQL resource.')
+			luci_log.debug_verbose('addMySQL2: %s' % str(e))
+	else:
+		try:
+			res = MySQL()
+			if not res:
+				raise Exception, 'could not create MySQL object'
+		except Exception, e:
+			errors.append('An error occurred while creating a MySQL resource.')
+			luci_log.debug_verbose('addMySQL3: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
+
+	try:
+		name = form['resourceName'].strip()
+		if not name:
+			raise Exception, 'No name was given for this MySQL resource.'
+		res.addAttribute('name', name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addMySQL4: %s' % err)
+
+	try:
+		config_file = form['config_file'].strip()
+		if not config_file:
+			raise KeyError, 'No path to the MySQL configuration file was given.'
+		res.addAttribute('config_file', config_file)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addMySQL5: %s' % err)
+
+	try:
+		listen_addr = form['listen_address'].strip()
+		if not listen_addr:
+			raise KeyError, 'No address was given for MySQL server to listen on.'
+		res.addAttribute('listen_address', listen_addr)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addMySQL6: %s' % err)
+
+	try:
+		options = form['mysql_options'].strip()
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('mysql_options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('mysql_options')
+		except:
+			pass
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addMySQL7: %s' % err)
+
+	try:
+		shutdown_wait = int(form['shutdown_wait'].strip())
+		res.addAttribute('shutdown_wait', str(shutdown_wait))
+	except KeyError, e:
+		res.addAttribute('shutdown_wait', '0')
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addMySQL7: %s' % err)
+
+	if len(errors) > 1:
+		return [None, None, errors]
+	return [res, model, None]
+
+def addOpenLDAP(request, form=None):
+	errors = list()
+
+	if form is None:
+		form = request.form
+
+	if not form:
+		luci_log.debug_verbose('addOpenLDAP0: form is missing')
+		return None
+
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addOpenLDAP1: model is missing')
+		return None
+
+	res = None
+	if form.has_key('edit'):
+		try:
+			oldname = form['oldname'].strip()
+			if not oldname:
+				raise Exception, 'oldname is blank.'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e:
+				errors.append('No OpenLDAP resource named \"%s\" exists.' % oldname)
+		except Exception, e:
+			errors.append('No original name was found for this OpenLDAP resource.')
+			luci_log.debug_verbose('addOpenLDAP2: %s' % str(e))
+	else:
+		try:
+			res = OpenLDAP()
+			if not res:
+				raise Exception, 'could not create OpenLDAP object'
+		except Exception, e:
+			errors.append('An error occurred while creating an OpenLDAP resource.')
+			luci_log.debug_verbose('addOpenLDAP3: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
+
+	try:
+		name = form['resourceName'].strip()
+		if not name:
+			raise Exception, 'No name was given for this OpenLDAP resource.'
+		res.addAttribute('name', name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addOpenLDAP4: %s' % err)
+
+	try:
+		url_list = form['url_list'].strip()
+		if not url_list:
+			raise KeyError, 'No URL list was given for this OpenLDAP resource.'
+		res.addAttribute('url_list', url_list)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addOpenLDAP5: %s' % err)
+
+	try:
+		config_file = form['config_file'].strip()
+		if not config_file:
+			raise KeyError, 'No path to the OpenLDAP configuration file was given.'
+		res.addAttribute('config_file', config_file)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addOpenLDAP6: %s' % err)
+
+	try:
+		options = form['slapd_options'].strip()
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('slapd_options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('slapd_options')
+		except:
+			pass
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addOpenLDAP7: %s' % err)
+
+	try:
+		shutdown_wait = int(form['shutdown_wait'].strip())
+		res.addAttribute('shutdown_wait', str(shutdown_wait))
+	except KeyError, e:
+		res.addAttribute('shutdown_wait', '0')
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addOpenLDAP7: %s' % err)
+
+	if len(errors) > 1:
+		return [None, None, errors]
+	return [res, model, None]
+
+def addPostgres8(request, form=None):
+	errors = list()
+
+	if form is None:
+		form = request.form
+
+	if not form:
+		luci_log.debug_verbose('addPostgreSQL80: form is missing')
+		return None
+
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addPostgreSQL81: model is missing')
+		return None
+
+	res = None
+	if form.has_key('edit'):
+		try:
+			oldname = form['oldname'].strip()
+			if not oldname:
+				raise Exception, 'oldname is blank.'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e:
+				errors.append('No PostgreSQL 8 resource named \"%s\" exists.' % oldname)
+		except Exception, e:
+			errors.append('No original name was found for this PostgreSQL 8 resource.')
+			luci_log.debug_verbose('addPostgreSQL82: %s' % str(e))
+	else:
+		try:
+			res = Postgres8()
+			if not res:
+				raise Exception, 'could not create PostgreSQL 8 object'
+		except Exception, e:
+			errors.append('An error occurred while creating a PostgreSQL 8 resource.')
+			luci_log.debug_verbose('addPostgreSQL83: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
+
+	try:
+		name = form['resourceName'].strip()
+		if not name:
+			raise Exception, 'No name was given for this PostgreSQL 8 resource.'
+		res.addAttribute('name', name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addPostgreSQL84: %s' % err)
+
+	try:
+		user = form['postmaster_user'].strip()
+		if not user:
+			raise KeyError, 'No postmaster user was given for this PostgreSQL 8 resource.'
+		res.addAttribute('postmaster_user', user)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addPostgreSQL85: %s' % err)
+
+	try:
+		config_file = form['config_file'].strip()
+		if not config_file:
+			raise KeyError, 'No path to the PostgreSQL 8 configuration file was given.'
+		res.addAttribute('config_file', config_file)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addPostgreSQL86: %s' % err)
+
+	try:
+		options = form['postmaster_options'].strip()
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('postmaster_options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('postmaster_options')
+		except:
+			pass
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addPostgreSQL87: %s' % err)
+
+	try:
+		shutdown_wait = int(form['shutdown_wait'].strip())
+		res.addAttribute('shutdown_wait', str(shutdown_wait))
+	except KeyError, e:
+		res.addAttribute('shutdown_wait', '0')
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addPostgreSQL87: %s' % err)
+
+	if len(errors) > 1:
+		return [None, None, errors]
+	return [res, model, None]
+
+def addTomcat5(request, form=None):
+	errors = list()
+
+	if form is None:
+		form = request.form
+
+	if not form:
+		luci_log.debug_verbose('addTomcat50: form is missing')
+		return None
+
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addTomcat51: model is missing')
+		return None
+
+	res = None
+	if form.has_key('edit'):
+		try:
+			oldname = form['oldname'].strip()
+			if not oldname:
+				raise Exception, 'oldname is blank.'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e:
+				errors.append('No Tomcat 5 resource named \"%s\" exists.' % oldname)
+		except Exception, e:
+			errors.append('No original name was found for this Tomcat 5 resource.')
+			luci_log.debug_verbose('addTomcat52: %s' % str(e))
+	else:
+		try:
+			res = Tomcat5()
+			if not res:
+				raise Exception, 'could not create Tomcat5 object'
+		except Exception, e:
+			errors.append('An error occurred while creating a Tomcat 5 resource.')
+			luci_log.debug_verbose('addTomcat53: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
+
+	try:
+		name = form['resourceName'].strip()
+		if not name:
+			raise Exception, 'No name was given for this Tomcat 5 resource.'
+		res.addAttribute('name', name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addTomcat54: %s' % err)
+
+	try:
+		user = form['tomcat_user'].strip()
+		if not user:
+			raise KeyError, 'No user was given for this Tomcat 5 resource.'
+		res.addAttribute('tomcat_user', user)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addTomcat55: %s' % err)
+
+	try:
+		config_file = form['config_file'].strip()
+		if not config_file:
+			raise KeyError, 'No path to the Tomcat 5 configuration file was given.'
+		res.addAttribute('config_file', config_file)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addTomcat56: %s' % err)
+
+	try:
+		options = form['catalina_options'].strip()
+		if not options:
+			raise KeyError, 'no options'
+		res.addAttribute('catalina_options', options)
+	except KeyError, e:
+		try:
+			res.removeAttribute('catalina_options')
+		except:
+			pass
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addTomcat57: %s' % err)
+
+	try:
+		catalina_base = form['catalina_base'].strip()
+		if not catalina_base:
+			raise KeyError, 'No cataliny base directory was given for this Tomcat 5 resource.'
+		res.addAttribute('catalina_base', catalina_base)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addTomcat58: %s' % err)
+
+	try:
+		shutdown_wait = int(form['shutdown_wait'].strip())
+		res.addAttribute('shutdown_wait', str(shutdown_wait))
+	except KeyError, e:
+		res.addAttribute('shutdown_wait', '0')
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addTomcat59: %s' % err)
+
+	if len(errors) > 1:
+		return [None, None, errors]
+	return [res, model, None]
+
+def addLVM(request, form=None):
+	errors = list()
+
+	if form is None:
+		form = request.form
+
+	if not form:
+		luci_log.debug_verbose('addLVM0: form is missing')
+		return None
+
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addLVM1: model is missing')
+		return None
+
+	res = None
+	if form.has_key('edit'):
+		try:
+			oldname = form['oldname'].strip()
+			if not oldname:
+				raise Exception, 'oldname is blank.'
+			try:
+				res = getResourceForEdit(model, oldname)
+			except KeyError, e:
+				errors.append('No LVM resource named \"%s\" exists.' % oldname)
+		except Exception, e:
+			errors.append('No original name was found for this LVM resource.')
+			luci_log.debug_verbose('addLVM2: %s' % str(e))
+	else:
+		try:
+			res = LVM()
+			if not res:
+				raise Exception, 'could not create LVM object'
+		except Exception, e:
+			errors.append('An error occurred while creating a LVM resource.')
+			luci_log.debug_verbose('addLVM3: %s' % str(e))
+
+	if not res:
+		return [None, None, errors]
+
+	try:
+		name = form['resourceName'].strip()
+		if not name:
+			raise Exception, 'No name was given for this LVM resource.'
+		res.addAttribute('name', name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addLVM4: %s' % err)
+
+	try:
+		vg_name = form['vg_name'].strip()
+		if not vg_name:
+			raise KeyError, 'No volume group name was given.'
+		res.addAttribute('vg_name', vg_name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addLVM5: %s' % err)
+
+	try:
+		lv_name = form['lv_name'].strip()
+		if not lv_name:
+			raise KeyError, 'No logical volume name was given.'
+		res.addAttribute('lv_name', lv_name)
+	except Exception, e:
+		err = str(e)
+		errors.append(err)
+		luci_log.debug_verbose('addLVM6: %s' % err)
+
+	if len(errors) > 1:
+		return [None, None, errors]
+	return [res, model, None]
+
+resourceAddHandler = {
+	'ip': addIp,
+	'fs': addFs,
+	'gfs': addGfs,
+	'nfsm': addNfsm,
+	'nfsx': addNfsx,
+	'nfsc': addNfsc,
+	'scr': addScr,
+	'smb': addSmb,
+	'tomcat-5': addTomcat5,
+	'postgres-8': addPostgres8,
+	'apache': addApache,
+	'openldap': addOpenLDAP,
+	'lvm': addLVM,
+	'mysql': addMySQL
+}
+
+def resolveClusterChanges(self, clusterName, model):
+	try:
 		mb_nodes = model.getNodes()
 		if not mb_nodes or not len(mb_nodes):
 			raise Exception, 'node list is empty'
@@ -5962,7 +7335,7 @@
 			messages.append('A new cluster node, \"%s,\" is now a member of cluster \"%s,\". but it has not been added to the management interface for this cluster as a result of an error creating a database entry for it.' % (i, clusterName))
 			luci_log.debug_verbose('VCC5: addFolder: %s/%s: %s' \
 				% (clusterName, i, str(e)))
-	
+
 	return messages
 
 def addResource(self, request, model, res, res_type):
@@ -5983,8 +7356,6 @@
 		return 'Unable to add the new resource'
 
 	try:
-		cp = model.getClusterPtr()
-		cp.incrementConfigVersion()
 		model.setModified(True)
 		conf = model.exportModelAsString()
 		if not conf:
@@ -6014,7 +7385,17 @@
 		res_name = res.attr_hash['address']
 
 	try:
-		set_node_flag(self, clustername, ragent, str(batch_number), RESOURCE_ADD, "Creating New Resource \'%s\'" % res_name)
+		try:
+			if request.form.has_key('edit'):
+				action_type = RESOURCE_CONFIG
+				action_str = 'Configuring resource \"%s\"' % res_name
+			else:
+				raise Exception, 'new'
+		except Exception, e:
+			action_type = RESOURCE_ADD
+			action_str = 'Creating new resource \"%s\"' % res_name
+
+		set_node_flag(self, clustername, ragent, str(batch_number), action_type, action_str)
 	except Exception, e:
 		luci_log.debug_verbose('addResource7: failed to set flags: %s' % str(e))
 
@@ -6049,7 +7430,7 @@
 		request.SESSION.set('model', model)
 	except:
 		luci_log.debug_verbose('Appending model to request failed')
-		return 'An error occurred while storing the cluster model.' 
+		return 'An error occurred while storing the cluster model.'
 
 def resolve_nodename(self, clustername, nodename):
 	path = str(CLUSTER_FOLDER_PATH + clustername)
@@ -6058,7 +7439,7 @@
 		clusterfolder = self.restrictedTraverse(path)
 		objs = clusterfolder.objectItems('Folder')
 	except Exception, e:
-		luci_log.info('RNN0: error for %s/%s: %s' \
+		luci_log.debug_verbose('RNN0: error for %s/%s: %s' \
 			% (nodename, clustername, str(e)))
 		return nodename
 
@@ -6069,7 +7450,7 @@
 		except:
 			continue
 
-	luci_log.info('RNN1: failed for %s/%s: nothing found' \
+	luci_log.debug_verbose('RNN1: failed for %s/%s: nothing found' \
 		% (nodename, clustername))
 	return nodename
 
@@ -6106,7 +7487,7 @@
 			if finished == -1:
 				luci_log.debug_verbose('NNFP2: batch error: %s' % batch_ret[1])
 			try:
-				nodefolder.manage_delObjects(item[0])
+				nodefolder.manage_delObjects([item[0]])
 			except Exception, e:
 				luci_log.info('NNFP3: manage_delObjects for %s failed: %s' \
 					% (item[0], str(e)))
@@ -6206,7 +7587,7 @@
 			msg += 'Fix the error and try again:\n'
 		else:
 			msg += 'PASSED\n'
-			
+
 			msg += 'Making sure no clustername change has accured - '
 			new_name = cc_xml.firstChild.getAttribute('name')
 			if new_name != clustername:
@@ -6214,13 +7595,13 @@
 				msg += 'Fix the error and try again:\n'
 			else:
 				msg += 'PASSED\n'
-				
+
 				msg += 'Increasing cluster version number - '
 				version = cc_xml.firstChild.getAttribute('config_version')
 				version = int(version) + 1
 				cc_xml.firstChild.setAttribute('config_version', str(version))
 				msg += 'DONE\n'
-				
+
 				msg += 'Propagating new cluster.conf'
 				rc = getRicciAgent(self, clustername)
 				if not rc:
@@ -6231,7 +7612,7 @@
 					if batch_id is None or result is None:
 						luci_log.debug_verbose('VFA: setClusterConf: batchid or result is None')
 						msg += '\nUnable to propagate the new cluster configuration for ' + clustername + '\n\n'
-					else:	
+					else:
 						msg += ' - DONE\n'
 						cc = cc_xml.toxml()
 						msg += '\n\nALL DONE\n\n'
--- conga/luci/site/luci/Extensions/conga_constants.py	2007/01/15 18:21:50	1.19.2.7
+++ conga/luci/site/luci/Extensions/conga_constants.py	2007/04/12 17:19:19	1.19.2.7.2.1
@@ -13,8 +13,8 @@
 NODE_ADD="15"
 NODE_PROCESS="16"
 NODE_LOGS="17"
-XENVM_ADD="18"
-XENVM_CONFIG="19"
+VM_ADD="18"
+VM_CONFIG="19"
 SERVICES="20"
 SERVICE_ADD="21"
 SERVICE_LIST="22"
@@ -24,7 +24,7 @@
 SERVICE_START="26"
 SERVICE_STOP="27"
 SERVICE_RESTART="28"
-XENVM_PROCESS="29"
+VM_PROCESS="29"
 RESOURCES="30"
 RESOURCE_ADD="31"
 RESOURCE_LIST="32"
@@ -44,8 +44,9 @@
 FENCEDEV="54"
 CLUSTER_DAEMON="55"
 SERVICE_DELETE = '56'
-FENCEDEV_DELETE = "57"
+FENCEDEV_DELETE = '57'
 FENCEDEV_NODE_CONFIG = '58'
+SERVICE_MIGRATE = '59'
 
 CONF_EDITOR = '80'
 
@@ -73,6 +74,7 @@
 PROP_FENCE_TAB = '2'
 PROP_MCAST_TAB = '3'
 PROP_QDISK_TAB = '4'
+PROP_GULM_TAB = '5'
 
 PAGETYPE="pagetype"
 ACTIONTYPE="actiontype"
--- conga/luci/site/luci/Extensions/homebase_adapters.py	2007/01/10 22:53:56	1.34.2.11
+++ conga/luci/site/luci/Extensions/homebase_adapters.py	2007/04/12 17:19:19	1.34.2.11.2.1
@@ -278,7 +278,7 @@
 		if not cluster_info:
 			errmsg = 'An error occurred while attempting to retrieve the cluster.conf file from \"%s\"' % cur_host
 		else:
-			errmsg = '\"%s\" reports is not a member of any cluster.'
+			errmsg = '\"%s\" reports is not a member of any cluster.' % cur_host
 		return (False, { 'errors': [ errmsg ] })
 
 	cluster_name = cluster_info[0]
@@ -571,7 +571,7 @@
 				if not prev_auth:
 					try:
 						rc.unauth()
-					except:
+					except Exception, e:
 						luci_log.debug_verbose('VAC4: %s: %s' % (cur_host, str(e)))
 
 				errors.append(err_msg)
--- conga/luci/site/luci/Extensions/ricci_bridge.py	2007/01/08 19:49:20	1.30.2.17
+++ conga/luci/site/luci/Extensions/ricci_bridge.py	2007/04/12 17:19:19	1.30.2.17.2.1
@@ -47,7 +47,8 @@
 			install_services,
 			install_shared_storage,
 			install_LVS,
-			upgrade_rpms):
+			upgrade_rpms,
+			gulm):
 	
 	batch = '<?xml version="1.0" ?>'
 	batch += '<batch>'
@@ -63,7 +64,10 @@
 	batch += '"/>'
 	batch += '<var name="sets" type="list_xml">'
 	if install_base or install_services or install_shared_storage:
-		batch += '<set name="Cluster Base"/>'
+		if gulm:
+			batch += '<set name="Cluster Base - Gulm"/>'
+		else:
+			batch += '<set name="Cluster Base"/>'
 	if install_services:
 		batch += '<set name="Cluster Service Manager"/>'
 	if install_shared_storage:
@@ -80,7 +84,10 @@
 	batch += '<function_call name="disable">'
 	batch += '<var mutable="false" name="services" type="list_xml">'
 	if install_base or install_services or install_shared_storage:
-		batch += '<set name="Cluster Base"/>'
+		if gulm:
+			batch += '<set name="Cluster Base - Gulm"/>'
+		else:
+			batch += '<set name="Cluster Base"/>'
 	if install_services:
 		batch += '<set name="Cluster Service Manager"/>'
 	if install_shared_storage:
@@ -112,7 +119,12 @@
 	batch += '<var mutable="false" name="propagate" type="boolean" value="false"/>'
 	batch += '<var mutable="false" name="cluster.conf" type="xml">'
 	batch += '<cluster config_version="1" name="' + cluster_name + '">'
-	batch += '<fence_daemon post_fail_delay="0" post_join_delay="3"/>'
+
+	if os_str == 'rhel4':
+		batch += '<fence_daemon post_fail_delay="0" post_join_delay="3"/>'
+	else:
+		batch += '<fence_daemon post_fail_delay="0" post_join_delay="12"/>'
+
 	batch += '<clusternodes/>'
 	batch += '<cman/>'
 	batch += '<fencedevices/>'
@@ -153,7 +165,8 @@
 		       install_services,
 		       install_shared_storage,
 		       install_LVS,
-		       upgrade_rpms):
+		       upgrade_rpms,
+		       gulm_lockservers):
 	
 	batch = '<?xml version="1.0" ?>'
 	batch += '<batch>'
@@ -169,7 +182,10 @@
 	batch += '"/>'
 	batch += '<var name="sets" type="list_xml">'
 	if install_base or install_services or install_shared_storage:
-		batch += '<set name="Cluster Base"/>'
+		if gulm_lockservers:
+			batch += '<set name="Cluster Base - Gulm"/>'
+		else:
+			batch += '<set name="Cluster Base"/>'
 	if install_services:
 		batch += '<set name="Cluster Service Manager"/>'
 	if install_shared_storage:
@@ -186,7 +202,10 @@
 	batch += '<function_call name="disable">'
 	batch += '<var mutable="false" name="services" type="list_xml">'
 	if install_base or install_services or install_shared_storage:
-		batch += '<set name="Cluster Base"/>'
+		if gulm_lockservers:
+			batch += '<set name="Cluster Base - Gulm"/>'
+		else:
+			batch += '<set name="Cluster Base"/>'
 	if install_services:
 		batch += '<set name="Cluster Service Manager"/>'
 	if install_shared_storage:
@@ -218,7 +237,12 @@
 	batch += '<var mutable="false" name="propagate" type="boolean" value="false"/>'
 	batch += '<var mutable="false" name="cluster.conf" type="xml">'
 	batch += '<cluster config_version="1" name="' + cluster_name + '" alias="' + cluster_alias + '">'
-	batch += '<fence_daemon post_fail_delay="0" post_join_delay="3"/>'
+
+	if os_str == 'rhel4':
+		batch += '<fence_daemon post_fail_delay="0" post_join_delay="3"/>'
+	else:
+		batch += '<fence_daemon post_fail_delay="0" post_join_delay="12"/>'
+
 	batch += '<clusternodes>'
 	x = 1
 	for i in nodeList:
@@ -228,12 +252,19 @@
 			batch += '<clusternode name="' + i + '" votes="1" nodeid="' + str(x) + '" />'
 		x = x + 1
 	batch += '</clusternodes>'
-	if len(nodeList) == 2:
-		batch += '<cman expected_votes="1" two_node="1"/>'
-	else:
-		batch += '<cman/>'
+
+	if not gulm_lockservers:
+		if len(nodeList) == 2:
+			batch += '<cman expected_votes="1" two_node="1"/>'
+		else:
+			batch += '<cman/>'
 	batch += '<fencedevices/>'
 	batch += '<rm/>'
+	if gulm_lockservers:
+		batch += '<gulm>'
+		for i in gulm_lockservers:
+			batch += '<lockserver name="%s" />' % i
+		batch += '</gulm>'
 	batch += '</cluster>'
 	batch += '</var>'
 	batch += '</function_call>'
@@ -433,6 +464,12 @@
 	ricci_xml = rc.batch_run(batch_str)
 	return batchAttemptResult(ricci_xml)
 
+def migrateService(rc, servicename, preferrednode):
+	batch_str = '<module name="cluster"><request API_version="1.0"><function_call name="migrate_service"><var mutable="false" name="servicename" type="string" value=\"' + servicename + '\"/><var mutable="false" name="nodename" type="string" value=\"' + preferrednode + '\" /></function_call></request></module>'
+
+	ricci_xml = rc.batch_run(batch_str)
+	return batchAttemptResult(ricci_xml)
+
 def updateServices(rc, enable_list, disable_list):
 	batch = ''
 
--- conga/luci/site/luci/Extensions/ricci_communicator.py	2007/01/04 00:22:13	1.9.2.9
+++ conga/luci/site/luci/Extensions/ricci_communicator.py	2007/04/12 17:19:19	1.9.2.9.2.1
@@ -165,7 +165,7 @@
             pass
 
         if not self.authed():
-            raise RicciError, 'not authenticated to host %s', self.__hostname
+            raise RicciError, 'not authenticated to host %s' % self.__hostname
         
         # construct request
         doc = minidom.Document()
Binary files /cvs/cluster/conga/luci/site/luci/var/Data.fs	2007/01/23 22:34:28	1.15.2.9 and /cvs/cluster/conga/luci/site/luci/var/Data.fs	2007/04/12 17:19:20	1.15.2.9.2.1 differ
rcsdiff: /cvs/cluster/conga/luci/site/luci/var/Data.fs: diff failed
/cvs/cluster/conga/luci/test/CGA_0160_Add_User.py,v  -->  standard output
revision 1.7.4.1
--- conga/luci/test/CGA_0160_Add_User.py
+++ -	2007-04-12 18:19:50.154218000 +0100
@@ -0,0 +1,219 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    CGA_0160_Add_User.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0
+Summary:        Test for Conga use case CGA-160 - Adding a new user 
+                http://sourceware.org/cluster/conga/usecases/cga-0160.html
+                
+                The sequence of actions is:
+                
+                As admin:
+                    Login
+                    Add systems
+                    Add users
+                    Assign user to a machine
+                    Logout
+  
+                As each user:  
+                    Login
+                    Verify user view machine storage
+                    Verify user cannot view other machines
+                    Logout
+  
+                As admin:
+                    Login
+                    Delete systems
+                    Delete users
+                    Logout
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+from selenium import selenium
+import unittest, time, re
+from conga_Helpers import *
+import time
+import logging
+from loggerObject import loggerObject
+
+class CGA_0160_Add_User (unittest.TestCase):
+
+    def setUp(self): 
+        
+        # Set up logging      
+        self.theloggerObject = setupLogger (CONGA_DEBUG_LOG)
+        self.logger = self.theloggerObject.getLogger()
+        self.logger.info('-----------------------------------------------------------') 
+             
+        """Establish connection to selenium server, login to luci """
+        self.verificationErrors = []
+        self.selenium = login (CONGA_ADMIN_USERNAME, CONGA_ADMIN_PASSWORD)
+
+        """Test to create and delete storage systems"""
+        sel = self.selenium
+
+        # Create the storage systems
+        for systemName in CONGA_STORAGE_SYSTEMS:
+            createStorageSystem(sel, systemName, CONGA_STORAGE_SYSTEMS[systemName], self.logger)
+            # Validation - verify that the success message was displayed for each storage system
+            self.assertEqual("Do you really want to add the following Storage Systems:\n" + systemName, sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Added storage system "' + systemName + '" successfully'))               
+        
+        # Return to homebase page
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)     
+
+        # Create the users
+        for userName in CONGA_USERS.keys(): 
+            createUser (sel, userName, CONGA_USERS[userName], self.logger)
+            # Validation - verify that the success message was displayed for each user
+            self.assertEqual('Do you really want to add the user "' + userName + '"?', sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Added new user "' + userName + '" successfully')) 
+     
+        # Return to homebase page
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)    
+
+        # Assign permissions to users as defined in CONGA_USERS_SYSTEMS
+        for userName in CONGA_USERS_SYSTEMS:
+            # Temporary - 20061205 - only tng3-2, 3-3, and 3-5 are reliable systems right now
+            #if ((userName == 'user2') or (userName == 'user3') or (userName == 'user5') ):
+                systemName = CONGA_USERS_SYSTEMS[userName]
+                # Replace . with _ in system name to match HTML
+                systemNameMod = systemName.replace('.', '_')
+
+                sel.click("link=User Permissions")
+                sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                
+                sel.select("userList", "label=" + userName)
+                sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                sel.click("name=__SYSTEM:" + systemNameMod)
+                
+                sel.click("document.adminform.elements['Update Permissions']")
+                sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                # The following statement is failing - not clear why - is luci not displaying the confirm dialog?
+                #self.assertEqual('Modify permissions for ' + userName, sel.get_confirmation())  - why is this failing?
+                sel.get_confirmation()
+                #print "DEBUG = " + userName + " " + systemName
+                self.assertTrue (sel.is_text_present('Added permission for ' + userName + ' for system ' + systemName))
+ 
+        # Logout as admin
+        logout(self.selenium)
+
+    def test_create_users_systems(self):
+        """Test to verify user permissions - control of access to view systems"""
+        
+        self.logger.info('Starting test case CGA_0160_Add_User.test_create_users_systems') 
+        
+        # Validation: Two part test:
+        #   1) Verify that the loggedin user can see the systems for which they have authorization
+        #   2) Verify that the loggedin user cannot see any other systems
+        for loggedInUser in CONGA_USERS:
+            self.selenium = login (loggedInUser, CONGA_USERS[loggedInUser])
+            sel = self.selenium 
+            sel.click("link=storage")
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            
+            for userName in CONGA_USERS_SYSTEMS:
+                    #print "DEBUG - loggedInUser=" + loggedInUser + " userName=" + userName
+                    if (loggedInUser == userName):
+                        # Validation 1 - seeing authorized systems
+                        self.logger.info('Verify user ' + userName + ' is able to access authorized systems')
+                        self.assertTrue (sel.is_text_present(CONGA_USERS_SYSTEMS[loggedInUser]))
+                                               
+                        sel.click("link=" + CONGA_USERS_SYSTEMS[loggedInUser])
+                        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                        
+                        # Comment out this code - 20061206 - this is causing the Zope
+                        # server to crash - need to investigate - maybe an internal
+                        # Zope timer?
+                        loopCounter = 1
+                        while loopCounter < 6:
+                            try:
+                                sel.click("link=Hard Drives")
+                                sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                                self.assertTrue (sel.is_text_present('hda'))
+                                break
+                            except:
+                                self.logger.info('Failed to view hard disk - assume slow reponse - trying again')
+                            loopCounter = loopCounter +1    
+                            time.sleep (30)
+                        
+                    else:
+                        # Validation 2 - not seeing other systems
+                        self.logger.info('Verify user ' + loggedInUser + ' is not able to access unauthorized system ' + CONGA_USERS_SYSTEMS[userName])
+                        self.assertFalse (sel.is_text_present(CONGA_USERS_SYSTEMS[userName]))
+                        
+            logout(self.selenium)  
+                   
+            self.logger.info('Ending test case CGA_0160_Add_User.test_create_users_systems') 
+
+    def tearDown(self):        
+        # login as admin
+        self.selenium = login (CONGA_ADMIN_USERNAME, CONGA_ADMIN_PASSWORD)
+        sel = self.selenium 
+        
+        # Return to homebase page
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)     
+
+        # View the defined storage systems 
+        sel.select_window("null")
+        sel.click("link=Manage Systems")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+
+        # Delete the storage systems
+        for systemName in CONGA_STORAGE_SYSTEMS:
+            deleteStorageSystem(sel, systemName, self.logger)
+            # Validation - verify that the success message was displayed for each storage system
+            self.assertEqual("Do you really want to remove the following managed systems:\nStorage Systems:\n-" + systemName, sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Removed storage system "' + systemName + '" successfully'))
+ 
+        # Return to homebase page
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)     
+
+        # Delete the users 
+        for userName in CONGA_USERS.keys(): 
+            deleteUser (sel, userName, self.logger)
+            # Validation - verify that the success message was displayed for each user
+            self.assertEqual('Do you really want to remove the user "' + userName + '"?', sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('User "' + userName + '" has been deleted'))  
+       
+        """Logout and stop Selenium session"""
+        logout(self.selenium)
+        self.assertEqual([], self.verificationErrors)
+        closeLogger (self.theloggerObject)
+
+def suite():
+        suite = unittest.TestSuite()
+        suite.addTest(CGA_0160_Add_User('test_create_users_systems'))
+        return suite
+
+if __name__ == "__main__":
+    #unittest.main()
+    unittest.TextTestRunner( verbosity=2 ).run( suite() )
/cvs/cluster/conga/luci/test/CGA_0170_Online_Documentation_Portlet.py,v  -->  standard output
revision 1.1.6.1
--- conga/luci/test/CGA_0170_Online_Documentation_Portlet.py
+++ -	2007-04-12 18:19:50.371239000 +0100
@@ -0,0 +1,91 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    GA_0170_Online_Documentation_Portlet.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0
+Summary:        Test for Conga use case CGA-0170 - verify the online user documentation
+                Regression test for: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=212991
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+from selenium import selenium
+import unittest, time, re
+from conga_Helpers import *
+import time
+import logging
+from loggerObject import loggerObject
+
+class CGA_0170_Online_Documentation_Portlet (unittest.TestCase):
+
+    def setUp(self):      
+               
+        # Set up logging      
+        self.theloggerObject = setupLogger (CONGA_DEBUG_LOG)
+        self.logger = self.theloggerObject.getLogger()
+        self.logger.info('-----------------------------------------------------------') 
+  
+        """Establish connection to selenium server, login to luci """
+        self.verificationErrors = []
+        self.selenium = login (CONGA_ADMIN_USERNAME, CONGA_ADMIN_PASSWORD)
+
+        """Test to create and delete storage systems"""
+        sel = self.selenium
+
+    def test_docs(self):
+        """Test to verify on-line Help docs"""
+        self.logger.info('Starting test case CGA_0170_Online_Documentation_Portlet.test_docs')
+        
+        sel = self.selenium
+        
+        # Access the on-line help
+        sel.click("link=help")
+        sel.wait_for_pop_up("Conga Help", "30000")        
+        sel.select_window('Conga Help')
+        
+        # Grab all the text       
+        theText = sel.get_html_source()
+
+        # Validation - check the offsets for the items in the HELP list             
+        for theItem in HELP_LIST:
+            self.logger.debug('Verify offset of help text "' + theItem + '" = ' + str(theText.index(theItem)))
+            self.assertEqual (theText.index(theItem), HELP_DICTIONARY[theItem])
+            
+        # Select the main window    
+        sel.select_window('null')
+        
+        self.logger.info('Ending test case CGA_0170_Online_Documentation_Portlet.test_docs')
+  
+    def tearDown(self):  
+        """Logout and stop Selenium session"""
+        logout(self.selenium)
+        self.assertEqual([], self.verificationErrors)
+        closeLogger (self.theloggerObject)
+
+def suite():
+        suite = unittest.TestSuite()
+        suite.addTest(CGA_0170_Online_Documentation_Portlet('test_docs'))
+        return suite
+
+if __name__ == "__main__":
+    #unittest.main()
+    unittest.TextTestRunner( verbosity=2 ).run( suite() )
/cvs/cluster/conga/luci/test/CGA_0200_Create_cluster.py,v  -->  standard output
revision 1.4.4.1
--- conga/luci/test/CGA_0200_Create_cluster.py
+++ -	2007-04-12 18:19:50.525882000 +0100
@@ -0,0 +1,141 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    CGA_0200_Create_cluster.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0 (http://www.openqa.org/selenium/)
+Summary:        Simple, demo/prototype test for Conga
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+from conga_Helpers import *
+from selenium import selenium
+import unittest, time, re
+
+class CGA_0200_Create_cluster(unittest.TestCase):
+
+    def setUp(self):
+        # Set up logging      
+        self.theloggerObject = setupLogger (CONGA_DEBUG_LOG)
+        self.logger = self.theloggerObject.getLogger()
+        self.logger.info('-----------------------------------------------------------') 
+        
+        """Establish connection to selenium server, login to luci """
+        self.verificationErrors = []
+        self.selenium = login (CONGA_ADMIN_USERNAME, CONGA_ADMIN_PASSWORD)
+ 
+    def test_congaCluster(self):
+        """Test to create and delete a cluster"""
+
+        self.logger.info('Starting test case CGA_0200_Create_cluster.test_congaCluster')
+
+        # TODO - Need to generalize this function - but as of 20061129, note tng3-1 
+        # is not responding to the creation of a cluster
+        sel = self.selenium
+        sel.open("/luci/homebase")
+        sel.click("link=cluster")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=Create a New Cluster")
+
+        # Create the new "testCluster" cluster
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.type("clusterName", "testCluster")
+        self.logger.debug('Create cluster: testCluster')
+
+        # Add the nodes to the cluster
+        # Needed to generalize statements like this:
+        #     sel.type("__SYSTEM1:Addr", "tng3-2.lab.msp.redhat.com")
+        #     sel.type("__SYSTEM1:Passwd", "password")
+        #     sel.click("//input[@value='Add Another Row']")
+        systemCounter = 0
+        for systemName in CONGA_SMALL_CLUSTER_SYSTEMS.keys():
+            sel.type("__SYSTEM" + str(systemCounter) + ":Addr", systemName)
+            sel.type("__SYSTEM" + str(systemCounter) + ":Passwd", CONGA_SMALL_CLUSTER_SYSTEMS[systemName])
+            systemCounter = systemCounter + 1
+            if (systemCounter > 2):
+                sel.click("//input[@value='Add Another Row']")          
+        #sel.click("document.adminform.rhn_dl[1]")
+        sel.click("Submit")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+
+        self.assertEqual('Add the cluster "testCluster" to the Luci management interface?' , sel.get_confirmation())
+        #sel.click("link=testCluster")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+
+        # Wait 5 minutes for the cluster nodes to be rebooted
+        time.sleep (300)
+
+        # Validation - verify that the newly created cluster shows up in the cluster list        
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=Manage Systems")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        self.assertTrue (sel.is_text_present('testCluster'))
+        self.assertTrue (sel.is_element_present("name=__CLUSTER:testCluster"))
+        
+        # Then - delete the cluster
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=cluster")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.select("cluster_action", "label=Delete this cluster")
+        sel.click("//input[@value='Go']")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=Manage Systems")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)       
+
+        # Delete the storage systems created when the cluster was created
+        for systemName in CONGA_SMALL_CLUSTER_SYSTEMS:
+            deleteStorageSystem(sel, systemName, self.logger)
+           # Validation - verify that the success message was displayed for each storage system
+            self.assertEqual("Do you really want to remove the following managed systems:\nStorage Systems:\n-" + systemName, sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Removed storage system "' + systemName + '" successfully'))
+
+        # And the cluster reference in the storage system list too
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=Manage Systems")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("__CLUSTER0")
+        sel.click("document.adminform.Submit")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        self.assertEqual("Do you really want to remove the following managed systems:\nClusters:\n-testCluster", sel.get_confirmation())
+
+        self.logger.info('Ending test case CGA_0200_Create_cluster.test_congaCluster')
+
+    def tearDown(self):
+        """Logout and stop Selenium session"""
+        logout(self.selenium)
+        self.assertEqual([], self.verificationErrors)
+        closeLogger (self.theloggerObject)
+
+def suite():
+        suite = unittest.TestSuite()
+        suite.addTest(CGA_0200_Create_cluster('test_congaCluster'))
+        return suite
+
+if __name__ == "__main__":
+    #unittest.main()
+    unittest.TextTestRunner(verbosity=2).run(suite())
/cvs/cluster/conga/luci/test/cleaner.py,v  -->  standard output
revision 1.1.6.1
--- conga/luci/test/cleaner.py
+++ -	2007-04-12 18:19:50.638191000 +0100
@@ -0,0 +1,102 @@
+#! /usr/bin/env python
+
+'''
+Script name:    cleaner.py
+Creation date:  Feb 2007
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0
+Summary:        Script to clean up all Conga test suite related artifacts - this script will be 
+                before Conga use case tests
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+from selenium import selenium
+import unittest, time, re
+from conga_Helpers import *
+import time
+import logging
+from loggerObject import loggerObject
+
+class cleaner:
+    
+    def cleanup(self):
+
+        selenium = login (CONGA_ADMIN_USERNAME, CONGA_ADMIN_PASSWORD)
+        sel = selenium
+        theloggerObject = setupLogger (CONGA_DEBUG_LOG)
+        logger = theloggerObject.getLogger()
+        
+        # Step 1 of 3 - Delete all clusters
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.click("link=cluster")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        
+        #####################
+            
+            
+        junk = sel.get_text("cluster_action")
+        print junk
+        print sel.get_body_text()
+        print '============================='
+        
+                
+        tempBool = sel.is_element_present("cluster_action")
+        if (tempBool == True):
+            sel.select("cluster_action", "label=Delete this cluster")
+            sel.click("//input[@value='Go']")
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            time.sleep(300)
+        else:
+            print 'No cluster to delete'
+           
+        sel.click("link=homebase")
+        
+        # Create the users
+#        for userName in CONGA_USERS.keys(): 
+#            createUser (sel, userName, CONGA_USERS[userName], logger)
+#            # Validation - verify that the success message was displayed for each user
+#            assertEqual('Do you really want to add the user "' + userName + '"?', sel.get_confirmation())
+#            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+#            assertTrue (sel.is_text_present('Added new user "' + userName + '" successfully')) 
+#     
+###############################################################################
+        # Delete the users 
+        for userName in CONGA_USERS.keys(): 
+            deleteUser (sel, userName, logger)
+            # Validation - verify that the success message was displayed for each user
+            try:
+                assertEqual('Do you really want to remove the user "' + userName + '"?', sel.get_confirmation())
+                sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                assertTrue (sel.is_text_present('User "' + userName + '" has been deleted'))  
+            except:
+                print 'Unable to delete user: ' + userName + ' does it exist?'
+    
+        
+#        # Create the storage systems
+#        for systemName in CONGA_STORAGE_SYSTEMS:
+#            createStorageSystem(sel, systemName, CONGA_STORAGE_SYSTEMS[systemName], logger)
+#            # Validation - verify that the success message was displayed for each storage system
+#            assertEqual("Do you really want to add the following Storage Systems:\n" + systemName, sel.get_confirmation())
+#            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+#            assertTrue (sel.is_text_present('Added storage system "' + systemName + '" successfully'))            
+#            
+
+        tempBool = sel.is_element_present("link=Manage Systems")
+        if (tempBool == True):
+             # Delete the storage systems
+            for systemName in CONGA_STORAGE_SYSTEMS:
+                deleteStorageSystem(sel, systemName, logger)
+                try:
+                    # Validation - verify that the success message was displayed for each storage system
+                    assertEqual("Do you really want to remove the following managed systems:\nStorage Systems:\n-" + systemName, sel.get_confirmation())
+                    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+                    assertTrue (sel.is_text_present('Removed storage system "' + systemName + '" successfully'))
+                except:
+                    print 'Unable to delete system: ' + systemName + ' does it exist?'
+        else:
+            print 'No systems to delete'
+           
+        sel = selenium.stop()
+             
\ No newline at end of file
/cvs/cluster/conga/luci/test/congaDemoTests.py,v  -->  standard output
revision 1.11.4.1
--- conga/luci/test/congaDemoTests.py
+++ -	2007-04-12 18:19:50.768470000 +0100
@@ -0,0 +1,128 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    congaDemoTests.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0 (http://www.openqa.org/selenium/)
+Summary:        Simple, demo/prototype test for Conga
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+from conga_Helpers import *
+from selenium import selenium
+import unittest, time, re
+import logging
+from loggerObject import loggerObject
+from cleaner import *
+
+class congaDemoTests(unittest.TestCase):
+
+    def setUp(self):
+        
+        temp = cleaner()    
+        temp.cleanup()
+        
+        # Set up logging      
+        self.theloggerObject = setupLogger (CONGA_DEBUG_LOG)
+        self.logger = self.theloggerObject.getLogger()
+        self.logger.info('-----------------------------------------------------------') 
+        
+        """Establish connection to selenium server, login to luci """
+        self.verificationErrors = []
+        self.selenium = login (CONGA_ADMIN_USERNAME, CONGA_ADMIN_PASSWORD)     
+ 
+    def test_congaStorage(self):
+        """Test to create and delete storage systems"""
+        self.logger.info('Starting test case congaDemoTests.test_congaStorage')    
+        sel = self.selenium
+
+        # Create the storage systems
+        for systemName in CONGA_STORAGE_SYSTEMS:
+            createStorageSystem(sel, systemName, CONGA_STORAGE_SYSTEMS[systemName], self.logger)
+            # Validation - verify that the success message was displayed for each storage system
+            self.assertEqual("Do you really want to add the following Storage Systems:\n" + systemName, sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Added storage system "' + systemName + '" successfully'))            
+            
+        # View the defined storage systems 
+        sel.select_window("null")
+        sel.click("link=Manage Systems")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+
+        # Delete the storage systems
+        for systemName in CONGA_STORAGE_SYSTEMS:
+            deleteStorageSystem(sel, systemName, self.logger)
+           # Validation - verify that the success message was displayed for each storage system
+            self.assertEqual("Do you really want to remove the following managed systems:\nStorage Systems:\n-" + systemName, sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Removed storage system "' + systemName + '" successfully'))
+
+        self.logger.info('Ending test case congaDemoTests.test_congaStorage')
+        
+    def test_congaUsers(self):
+        """Test to create and delete conga users"""
+        self.logger.info('Starting test case congaDemoTests.test_congaUsers')        
+        sel = self.selenium
+
+        # Create the users
+        for userName in CONGA_USERS.keys(): 
+            createUser (sel, userName, CONGA_USERS[userName], self.logger)
+            # Validation - verify that the success message was displayed for each user
+            self.assertEqual('Do you really want to add the user "' + userName + '"?', sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('Added new user "' + userName + '" successfully')) 
+
+        # Return to homebase page
+        sel.click("link=homebase")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)     
+
+        # Delete the users 
+        for userName in CONGA_USERS.keys(): 
+            deleteUser (sel, userName, self.logger)
+            # Validation - verify that the success message was displayed for each user
+            self.assertEqual('Do you really want to remove the user "' + userName + '"?', sel.get_confirmation())
+            sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+            self.assertTrue (sel.is_text_present('User "' + userName + '" has been deleted'))  
+
+        self.logger.info('Ending test case congaDemoTests.test_congaUsers')
+
+    def tearDown(self):
+        """Logout and stop Selenium session"""
+        logout(self.selenium)
+        self.assertEqual([], self.verificationErrors)
+        closeLogger (self.theloggerObject)
+
+def suite():
+        suite = unittest.TestSuite()
+#        suite.addTest(congaDemoTests('test_congaStorage'))
+        suite.addTest(congaDemoTests('test_congaUsers'))
+        return suite
+
+if __name__ == "__main__":
+    #unittest.main()
+    unittest.TextTestRunner(verbosity=2).run(suite())
+    
+# To write pyunit-generated output to file
+#    tests = unittest.defaultTestLoader.loadTestsFromTestCase(congaDemoTests) 
+#    output = open("/var/tmp/output-file.txt", "w") 
+#    unittest.TextTestRunner(output).run(tests) 
+#    output.close() 
/cvs/cluster/conga/luci/test/conga_Helpers.py,v  -->  standard output
revision 1.14.4.1
--- conga/luci/test/conga_Helpers.py
+++ -	2007-04-12 18:19:50.898464000 +0100
@@ -0,0 +1,193 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    conga_Helpers.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0 (http://www.openqa.org/selenium/)
+Summary:        Common routines, global variables
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+from selenium import selenium
+import unittest, time, re
+import logging
+import logging.handlers
+from loggerObject import loggerObject
+
+# Define data to support tests - global for now
+
+CONGA_ADMIN_USERNAME = 'admin'
+CONGA_ADMIN_PASSWORD = 'password'
+CONGA_SERVER = 'http://tng3-5.lab.msp.redhat.com:8080'
+PAGE_DISPLAY_DELAY = '60000'
+CONGA_LOG = '/var/tmp/congaTest.log'
+CONGA_DEBUG_LOG = '/var/tmp/congaTest_debug.log'
+
+# 20061130 - Node tng3-1 isn't booting, node tng3-4 is having some problems too
+
+CONGA_STORAGE_SYSTEMS = { #'tng3-1.lab.msp.redhat.com':'password', 
+                         #'tng3-2.lab.msp.redhat.com':'password',
+                         'tng3-3.lab.msp.redhat.com':'password',
+                         'tng3-4.lab.msp.redhat.com':'password',
+                         'tng3-5.lab.msp.redhat.com':'password'}
+
+CONGA_SMALL_CLUSTER_SYSTEMS = {  #'tng3-1.lab.msp.redhat.com':'password', 
+                         #'tng3-2.lab.msp.redhat.com':'password', 
+                         'tng3-3.lab.msp.redhat.com':'password',
+                         'tng3-4.lab.msp.redhat.com':'password' }
+
+CONGA_LARGE_CLUSTER_SYSTEMS = {#'tng3-1.lab.msp.redhat.com':'password', 
+                         #'tng3-2.lab.msp.redhat.com':'password', 
+                         'tng3-3.lab.msp.redhat.com':'password',
+                         'tng3-4.lab.msp.redhat.com':'password',
+                         'tng3-5.lab.msp.redhat.com':'password'}
+
+CONGA_USERS =           {#'user1':'user1_password', 
+                         #'user2':'user2_password', 
+                         'user3':'user3_password', 
+                         'user4':'user4_password', 
+                         'user5':'user5_password', 
+                         'user6':'user6_password', 
+                         'user7':'user7_password', 
+                         'user8':'user8_password', 
+                         'user9':'user9_password', 
+                         'user10':'user10_password'}
+
+CONGA_USERS_SYSTEMS =   {#'user1':'tng3-1.lab.msp.redhat.com', 
+                         #'user2':'tng3-2.lab.msp.redhat.com', 
+                         'user3':'tng3-3.lab.msp.redhat.com', 
+                         'user4':'tng3-4.lab.msp.redhat.com', 
+                         'user5':'tng3-5.lab.msp.redhat.com'
+                         }
+   
+# Data used to verify the on-line help contents. The Dictionary contains
+# strings and offsets of the strings in the help as of 20061207   
+HELP_LIST = ['Conga User Manual',
+             'Introduction', 
+             'Conga Architecture', 
+             'Homebase Tab', 
+             'Cluster Tab', 
+             'Storage Tab']
+        
+HELP_DICTIONARY = {'Conga User Manual':77,
+                   'Introduction':200,
+                   'Conga Architecture':225,
+                   'Homebase Tab':4257,
+                   'Cluster Tab':12280,
+                   'Storage Tab':23833 }
+
+def createStorageSystem(sel, systemName, systemPassword, theLogger):
+    """Common code to create storage systems"""
+    theLogger.debug ('Create storage system: ' + systemName)
+    sel.click("link=Add a System")
+    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+    sel.type("__SYSTEM0:Addr", systemName)        
+    sel.type("__SYSTEM0:Passwd", systemPassword)
+    sel.click("Submit")
+    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+
+def deleteStorageSystem(sel, systemName, theLogger):
+    """Common code to delete storage systems"""
+    try:
+        theLogger.debug('Delete storage system: ' + systemName)
+        # Need to handle artifacts names - underscores in strings, not periods
+        systemNameMod = systemName.replace('.', '_')
+        sel.click("name=__SYSTEM:" + systemNameMod)
+        sel.click("document.adminform.Submit")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)          
+    except:
+        print 'Unable to delete system: ' + systemName
+
+def createUser(sel, userName, userPassword, theLogger):
+    """Common code to create users"""
+    theLogger.debug('Create user: ' + userName)
+    sel.click("link=Add a User")
+    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+    sel.type("newUserName", userName)
+    sel.type("newPassword", userPassword)     
+    sel.type("newPasswordConfirm", userPassword)
+    sel.click("Submit")
+    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+    
+def deleteUser(sel, userName, theLogger):
+    """Common code to delete users"""
+    try:
+        theLogger.debug('Delete user: ' + userName)
+        sel.click("link=Delete a User")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+        sel.select("deluserId", "label=" + userName)
+        sel.click("Submit")
+        sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)    
+    except:
+        print 'Unable to delete user: ' + userName
+        
+def login(userName, password):
+    """Establish connection to selenium server, login to luci """
+    sel = selenium("localhost", 4444, "*firefox", CONGA_SERVER)
+    sel.start()
+
+    #  Login to the luci web app
+    sel.open(CONGA_SERVER)
+
+    # Wait for "Redirecting, please wait to complete" - it seems like this should not be needed,
+    # but as of 20061130, it still is
+    time.sleep(10)
+    sel.type("__ac_name", userName)
+    sel.type("__ac_password", password)
+    sel.click("submit")
+    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+    return sel
+
+def logout(sel):
+    """Logout and stop Selenium session"""
+    sel.click("link=Log out")
+    sel.wait_for_page_to_load(PAGE_DISPLAY_DELAY)
+    sel.stop()
+    
+def setupLogger (testName):   
+    """Create logger - use this message format:  2003-07-08 16:49:45,896 ERROR We have a problem
+    For now - 20061213 - just append all log messages to a single log file """
+    logger = logging.getLogger('congaTestLogger')
+    
+    # Why use a RotatingFileHandler? To keep track of previous test results - maybe?
+    #hdlr = logging.handlers.RotatingFileHandler(testName, 'a', 1024000, 10)
+    hdlr = logging.FileHandler(testName, 'a')
+    
+    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+    hdlr.setFormatter(formatter)
+    logger.addHandler(hdlr) 
+    #logger.setLevel(logging.WARNING)
+    logger.setLevel(logging.DEBUG)
+    
+    theloggerObject = loggerObject()
+    theloggerObject.setLogger(logger)
+    theloggerObject.setHandler(hdlr)
+    return theloggerObject
+    
+def closeLogger (theloggerObject):
+    """Close the logger - roll the logs"""
+    theLogger = theloggerObject.getLogger()
+    theHandler = theloggerObject.getHandler()
+    theLogger.removeHandler(theHandler)
+    theHandler.close()
+#    theHandler.doRollover()
+    
/cvs/cluster/conga/luci/test/conga_suite.py,v  -->  standard output
revision 1.11.4.1
--- conga/luci/test/conga_suite.py
+++ -	2007-04-12 18:19:51.274070000 +0100
@@ -0,0 +1,81 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    conga_suite.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0 (http://www.openqa.org/selenium/)
+Summary:        Simple, demo/prototype test for Conga
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+# Import the test suites
+import unittest
+import congaDemoTests
+from conga_Helpers import *
+import CGA_0160_Add_User
+import CGA_0170_Online_Documentation_Portlet
+import CGA_0200_Create_cluster
+
+# Tests planned - all not yet complete:   
+#   CGA-0160_Add_User.py
+#   CGA-0170_Online_Documenation_Portlet.py
+#   CGA-0180_Event_Portlet.py
+#   CGA-0200_Create_cluster.py
+#   CGA-0210_Add_cluster_to_Administer.py
+#   CGA-0220_Add_node_to_cluster.py
+#   CGA-0300_Initialize_Disk_or_Partition.py
+#   CGA-0310_Create_Volume_Group.py
+#   CGA-0311_Activate_Volume_Group.py
+#   CGA-0312_Deactivate_Volume_Group.py
+#   CGA-0313_Remove_Volume_Group.py
+#   CGA-0314_Add_Physical_Volume.py
+#   CGA-0315_Remove_Physical_Volume.py
+#   CGA-0320_Create_Logical_Volume.py
+#   CGA-0321_Remove_Logical_Volume.py
+#   CGA-0322_Extend_Logical_Volume.py
+#   CGA-0323_Reduce_Logical_Volume.py
+#   CGA-0330_Migrate_Data_off_Physical_Volume.py
+#   CGA-0331_Take_Snapshot_Volume.py
+#   CGA-0340_Set_logging_Params.py
+#   CGA-0341_Block_Device_Params.py
+#   CGA-0342_Backup_Archive_Params.py
+
+# Define the suite elements
+congaDemoSuite = congaDemoTests.suite()
+CGA_0160_Add_UserSuite = CGA_0160_Add_User.suite()
+CGA_0170_Online_Documentation_Portlet_Suite = CGA_0170_Online_Documentation_Portlet.suite()
+CGA_0200_Create_cluster_Suite = CGA_0200_Create_cluster.suite()
+
+# Assemble the suite
+suite = unittest.TestSuite()
+suite.addTest(congaDemoSuite)
+suite.addTest(CGA_0160_Add_UserSuite)
+suite.addTest(CGA_0170_Online_Documentation_Portlet_Suite)
+suite.addTest(CGA_0200_Create_cluster_Suite)
+
+# Run the test suite
+unittest.TextTestRunner(verbosity=2).run(suite)
+
+# Or, write all pyunit-generated messages to a log file
+#output = open(CONGA_LOG, "w") 
+#unittest.TextTestRunner(output).run(suite) 
+#output.close() 
/cvs/cluster/conga/luci/test/loggerObject.py,v  -->  standard output
revision 1.2.6.1
--- conga/luci/test/loggerObject.py
+++ -	2007-04-12 18:19:51.387367000 +0100
@@ -0,0 +1,50 @@
+#! /usr/bin/env python
+
+# Copyright Red Hat, Inc. 2006
+#
+#  This program is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by the
+#  Free Software Foundation; either version 2, or (at your option) any
+#  later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; see the file COPYING.  If not, write to the
+#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+#  MA 02139, USA.
+
+'''
+Script name:    loggerObject.py
+Creation date:  Dec 2006
+Purpose:        Prototype automated GUI test for RHEL5 Conga (luci server web app) - automated
+                with Selenium RC (remote control) 0.9.0 (http://www.openqa.org/selenium/)
+Summary:        Simple object to let us get/set the logger and its handler
+'''
+
+__author__ = 'Len DiMaggio <ldimaggi at redhat.com>'
+
+
+class loggerObject:
+    theLogger = 'null'
+    theHandler = 'null'
+    
+    # constructor - not sure if we want this
+    # def __init__(self, value):
+    #   self.name = value
+
+    # getters and setters methods
+    def setLogger( self, value ):
+        self.theLogger = value
+        
+    def setHandler( self, value ):
+        self.theHandler = value
+        
+    def getLogger( self ):
+        return self.theLogger
+    
+    def getHandler( self ):
+        return self.theHandler
/cvs/cluster/conga/luci/test/tests_README.txt,v  -->  standard output
revision 1.2.4.1
--- conga/luci/test/tests_README.txt
+++ -	2007-04-12 18:19:51.759116000 +0100
@@ -0,0 +1,86 @@
+README for Running RHEL5 Conga Automated GUI Tests
+==================================================
+
+(Rev 1.0 - 20061211, ldimaggi at redhat.com)
+
+1) Install Selenium Core and Selenium RC (Remote Control)
+   a. wget http://release.openqa.org/selenium-core/0.8.1/selenium-core-0.8.1.zip
+   b. wget http://release.openqa.org/selenium-remote-control/0.9.0/selenium-remote-control-0.9.0.zip
+   c. Unzip someplace - I used /opt
+
+2) Make sure Firefox 1.5 or newer is installed - I'm using 1.5.0.8
+
+3) Make sure Java 1.5 is installed - I'm using Sun 1.5.0_09
+
+4) Add Firefox bin dir to PATH
+   a. export PATH=/usr/lib/firefox-1.5.0.8:$PATH
+
+5) Start up Selenium server:
+   a. cd /opt/selenium/selenium-remote-control-0.9.0/server/
+   b. java -jar selenium-server.jar
+
+6) On the luci server, modify config to not use SSL, edit /var/lib/luci/etc/zope.conf:
+   a. Change this:
+
+           <cgi-environment> 
+           HTTPS ON
+           <cgi-environment> 
+
+      to this:
+
+           <cgi-environment> 
+           HTTPS OFF
+           <cgi-environment> 
+
+   b. Change this:
+
+      <http-server>                                              
+      # valid keys are "address" and "force-connection-close"    
+      #address 8080                                              
+      address localhost:25639                               
+      # force-connection-close on                           
+      </http-server>             
+
+     to this:
+
+      <http-server>       
+      address 8080                            
+      </http-server> 
+
+   c. Restart luci service
+
+7) Get a local copy of the tests.
+
+   a. mkdir sandbox; cd sandbox
+   b. cvs -d :ext:ldimaggi at sources.redhat.com:/cvs/cluster checkout conga
+   c. cd conga/luci/tests
+   
+8) Set up the test data to match your current test network node names
+   and passwords - edit conga_Helpers.py
+
+9) Run the tests individually:
+   a. python congaDemoTests.py
+   b. python CGA_0160_Add_User.py
+   c. python CGA_0170_Online_Documenation_Portlet.py
+   d. python CGA_0200_Create_cluster.py
+
+   Or, run all the tests as a suite:
+   a. python conga_suite.py
+
+   Look in the syslog and debug log on the luci server and in the selenium server log for errors if a test fails.
+
+Current Issues:
+===============
+
+   1) The tests that create a cluster (congaDemoTests.py,
+      CGA_0200_Create_cluster.py) don't yet fully cleanup after a test
+      run. The cluster is deleted in the luci database, but the
+      cluster.conf file and the service startup settings are not reset
+      on the cluster nodes.
+
+   2) The tests do not yet handle errors, other than expected failures. 
+
+
+
+
+
--- conga/luci/utils/luci_admin	2007/01/11 00:23:59	1.50.2.2
+++ conga/luci/utils/luci_admin	2007/04/12 17:19:31	1.50.2.2.2.1
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+# Copyright (C) 2006-2007 Red Hat, Inc.
+
 import sys, os, stat, select, string, pwd
 from sys import stderr, argv
 import types
--- conga/luci/utils/luci_cleanup	2006/08/04 18:43:26	1.4
+++ conga/luci/utils/luci_cleanup	2007/04/12 17:19:31	1.4.6.1
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+# Copyright (C) 2006-2007 Red Hat, Inc.
+
 import sys, os, pwd
 import types
 
@@ -34,7 +36,7 @@
 LUCI_BACKUP_DIR = '/var/lib/luci/var'
 LUCI_DB_PATH = '/var/lib/luci/var/Data.fs'
 
-null = file(os.devnull, 'rwb+', 0)
+null = file('/dev/null', 'rwb+', 0)
 orig_stderr = sys.stderr
 
 def restore_luci_db_fsattr():
--- conga/luci/utils/luci_manage	2007/01/08 19:27:35	1.1.2.1
+++ conga/luci/utils/luci_manage	2007/04/12 17:19:31	1.1.2.1.2.1
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+# Copyright (C) 2006-2007 Red Hat, Inc.
+
 import sys, os, pwd
 import types
 
@@ -34,7 +36,7 @@
 LUCI_BACKUP_DIR = '/var/lib/luci/var'
 LUCI_DB_PATH = '/var/lib/luci/var/Data.fs'
 
-null = file(os.devnull, 'rwb+', 0)
+null = file('/dev/null', 'rwb+', 0)
 orig_stderr = sys.stderr
 
 def restore_luci_db_fsattr():
--- conga/make/version.in	2007/01/23 22:34:37	1.21.2.13
+++ conga/make/version.in	2007/04/12 17:19:31	1.21.2.13.2.1
@@ -1,5 +1,5 @@
-VERSION=0.8
-RELEASE=30
+VERSION=0.9.2
+RELEASE=6
 # Remove "_UNRELEASED" at release time.
 # Put release num at the beggining, 
 # so that after it gets released, it has 
--- conga/ricci/ricci.spec.in.in	2006/05/26 22:17:35	1.12
+++ conga/ricci/ricci.spec.in.in	2007/04/12 17:19:31	1.12.4.1
@@ -85,7 +85,7 @@
 if [ ! -e /var/lib/ricci/certs/privkey.pem ]; then
 	/usr/bin/openssl genrsa -out /var/lib/ricci/certs/privkey.pem 2048 > /dev/null 2>&1
 	/usr/bin/openssl req -new -x509 -key /var/lib/ricci/certs/privkey.pem \
-	-out /var/lib/ricci/certs/cacert.pem -days 1095 -config /var/lib/ricci/certs/cacert.config
+	-out /var/lib/ricci/certs/cacert.pem -days 1825 -config /var/lib/ricci/certs/cacert.config
 	/bin/chown -R ricci:ricci /var/lib/ricci/certs/*
 	/bin/chmod 644 /var/lib/ricci/certs/*
 	/bin/chmod 400 /var/lib/ricci/certs/privkey.pem
/cvs/cluster/conga/ricci/common/Network.cpp,v  -->  standard output
revision 1.1.6.1
--- conga/ricci/common/Network.cpp
+++ -	2007-04-12 18:19:52.385507000 +0100
@@ -0,0 +1,84 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/*
+ * Author: Stanko Kupcevic <kupcevic at redhat.com>
+ */
+
+
+#include "Network.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+
+
+counting_auto_ptr<Network::Hostent> 
+Network::getHostByName(const String& hostname)
+{
+  counting_auto_ptr<Hostent> ent_d(new Hostent());
+  struct hostent *ent = 0;
+  int error;
+  gethostbyname2_r(hostname.c_str(), AF_INET, 
+		   &(ent_d->ent), 
+		   ent_d->data, sizeof(ent_d->data), 
+		   &ent, 
+		   &error);
+  if (ent == &(ent_d->ent))
+    return ent_d;
+  throw String("unable to resolve ") + hostname;
+}
+
+
+std::vector<String>
+Network::name2IP(const String& hostname)
+{
+  std::vector<String> addrs;
+  try {
+    char buff[INET_ADDRSTRLEN+1];
+    counting_auto_ptr<Hostent> hent = getHostByName(hostname);
+    char** addrs_b = (*hent)->h_addr_list;
+    for (int i=0; addrs_b[i]; i++) {
+      struct in_addr addr;
+      addr.s_addr = *((u_int32_t*) addrs_b[i]);
+      if (inet_ntop(AF_INET, &addr, buff, sizeof(buff)))
+	addrs.push_back(buff);
+    }
+  } catch ( ... ) {}
+  return addrs;
+}
+
+
+String
+Network::localhost()
+{
+  // get hostname
+  char name[1024];
+  if (gethostname(name, sizeof(name)-1))
+    return "";
+  name[sizeof(name)-1] = '\0';
+  
+  try {
+    // get fqdn
+    counting_auto_ptr<Hostent> ent = getHostByName(name);
+    return String((*ent)->h_name);
+  } catch ( ... ) {
+    return name;
+  }
+}
--- conga/ricci/common/ClientSocket.cpp	2006/08/10 22:53:07	1.4
+++ conga/ricci/common/ClientSocket.cpp	2007/04/12 17:19:31	1.4.6.1
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2005
+  Copyright Red Hat, Inc. 2005-2007
 
   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
@@ -23,6 +23,7 @@
 
 #include "Socket.h"
 #include "Logger.h"
+#include "Network.h"
 
 #include <unistd.h>
 #include <errno.h>
@@ -38,7 +39,7 @@
   Socket(-1)
 {}
 
-ClientSocket::ClientSocket(int sock, unsigned int addr) :
+ClientSocket::ClientSocket(int sock, u_int32_t addr) :
   Socket(sock),
   _addr(addr)
 {}
@@ -66,34 +67,57 @@
   //  log(msg, LogSocket);
 }
 
-ClientSocket::ClientSocket(const String& hostname, unsigned short port) :
+ClientSocket::ClientSocket(const String& hostname, 
+			   unsigned short port, 
+			   unsigned int timeout_ms) :
   Socket(-1)
 {
   _sock = socket(PF_INET, SOCK_STREAM, 0);
   if (_sock == -1)
-    throw String("ClientSocket(hostname, port): socket() failed");
+    throw String("ClientSocket(hostname, port, timeout): socket() failed");
   
-  struct hostent* ent = gethostbyname2(hostname.c_str(), AF_INET);
-  if (!ent)
-    throw String("ClientSocket(hostname, port): gethostbyname() failed");
+  if (timeout_ms)
+    nonblocking(true);
   
-  char** addrs = ent->h_addr_list;
+  counting_auto_ptr<Network::Hostent> ent = Network::getHostByName(hostname);
+  
+  char** addrs = (*ent)->h_addr_list;
   for (int i=0; addrs[i]; i++) {
     struct sockaddr_in addr_in;
     addr_in.sin_family = AF_INET;
     addr_in.sin_port = htons(port);
     addr_in.sin_addr.s_addr = *((u_int32_t*) addrs[i]);
-    if (connect(_sock, (struct sockaddr*) &addr_in, sizeof(addr_in)))
-      continue;
-    else {
-      //      String msg = String("created client socket ") + _sock;
-      //      msg += ", and connected to " + hostname + ", port " + port;
-      //      log(msg, LogSocket);
+    
+    if (connect(_sock, (struct sockaddr*) &addr_in, sizeof(addr_in)) == 0) {
+      // connected
+      nonblocking(false);
       _addr = addr_in.sin_addr.s_addr;
       return;
     }
+    
+    // connect() error
+    if (errno != EINPROGRESS)
+      continue;
+    bool can_read=false, can_write=true;
+    poll(can_read, can_write, timeout_ms);
+    if (can_write == false) {
+      // connect() not completed
+      throw String("ClientSocket(hostname, port, timeout): connect() timed out");
+    }
+    // connect() completed, check successfulness
+    int err = 1;
+    socklen_t err_size = sizeof(err);
+    getsockopt(_sock, SOL_SOCKET, SO_ERROR,
+	       &err, &err_size);
+    if (err)
+      continue;
+    
+    // connected
+    nonblocking(false);
+    _addr = addr_in.sin_addr.s_addr;
+    return;
   }
-  throw String("ClientSocket(hostname, port): connect() failed");
+  throw String("ClientSocket(hostname, port, timeout): connect() failed");
 }
 
 ClientSocket::ClientSocket(const ClientSocket& s) :
@@ -118,14 +142,13 @@
 bool 
 ClientSocket::connected_to(const String& hostname)
 {
-  struct hostent* ent = gethostbyname2(hostname.c_str(), AF_INET);
-  if (!ent)
-    return false;
-  
-  char** addrs = ent->h_addr_list;
-  for (int i=0; addrs[i]; i++)
-    if (*((u_int32_t*) addrs[i]) == _addr)
-      return true;
+  try {
+    counting_auto_ptr<Network::Hostent> ent = Network::getHostByName(hostname);
+    char** addrs = (*ent)->h_addr_list;
+    for (int i=0; addrs[i]; i++)
+      if (*((u_int32_t*) addrs[i]) == _addr)
+        return true;
+  } catch ( ... ) {}
   return false;
 }
 
@@ -153,10 +176,23 @@
     
     //    log(String("received ") + ret + " bytes from socket " + _sock, 
     //	LogLevel(LogSocket|LogTransfer));
-    return String(buffer, ret);
+    String data(buffer, ret);
+    shred(buffer, ret);
+    return data;
   }
 }
 
+String
+ClientSocket::recv(int timeout)
+{
+  bool in=true, out=false;
+  poll(in, out, timeout);
+  if (in)
+    return recv();
+  else
+    return "";
+}
+
 String 
 ClientSocket::send(const String& msg)
 {
@@ -171,7 +207,7 @@
       else if (errno == EAGAIN ||
 	       errno == EWOULDBLOCK)
 	return msg;
-      throw String("ClientSocket::recv(): socket error");
+      throw String("ClientSocket::send(): socket error");
     }
     
     //    log(String("sent ") + ret + " bytes thru socket " + _sock, 
@@ -180,6 +216,17 @@
   }
 }
 
+String
+ClientSocket::send(const String& msg, int timeout)
+{
+  bool in=false, out=true;
+  poll(in, out, timeout);
+  if (out)
+    return send(msg);
+  else
+    return msg;
+}
+
 void 
 ClientSocket::ready(bool& recv, bool& send, int timeout)
 {
--- conga/ricci/common/Makefile	2006/10/23 21:13:20	1.6.2.1
+++ conga/ricci/common/Makefile	2007/04/12 17:19:31	1.6.2.1.2.1
@@ -24,10 +24,11 @@
 	utils.o \
 	File.o \
 	XML.o \
+	Network.o \
 	Socket.o \
 	ServerSocket.o \
-	Logger.o \
 	ClientSocket.o \
+	Logger.o \
 	Variable.o \
 	Random.o \
 	daemon_init.o \
--- conga/ricci/common/Socket.cpp	2006/08/10 22:53:07	1.3
+++ conga/ricci/common/Socket.cpp	2007/04/12 17:19:31	1.3.6.1
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2005
+  Copyright Red Hat, Inc. 2005-2007
 
   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
@@ -181,23 +181,3 @@
     }
   }
 }
-
-
-
-std::vector<String>
-name2IP(const String& hostname)
-{
-  char buff[INET_ADDRSTRLEN+1];
-  std::vector<String> addrs;
-  struct hostent* ent = gethostbyname2(hostname.c_str(), AF_INET);
-  if (!ent)
-    return addrs;
-  char** addrs_b = ent->h_addr_list;
-  for (int i=0; addrs_b[i]; i++) {
-    struct in_addr addr;
-    addr.s_addr = *((u_int32_t*) addrs_b[i]);
-    if (inet_ntop(AF_INET, &addr, buff, sizeof(buff)))
-      addrs.push_back(buff);
-  }
-  return addrs;
-}
--- conga/ricci/common/Time.cpp	2006/08/10 22:53:07	1.4
+++ conga/ricci/common/Time.cpp	2007/04/12 17:19:31	1.4.6.1
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2005
+  Copyright Red Hat, Inc. 2005-2007
 
   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
@@ -31,8 +31,7 @@
 time_sec()
 {
   struct timeval t;
-  struct timezone z;
-  gettimeofday(&t, &z);
+  gettimeofday(&t, NULL);
   return t.tv_sec;
 }
 
@@ -40,8 +39,7 @@
 time_mil()
 {
   struct timeval t;
-  struct timezone z;
-  gettimeofday(&t, &z);
+  gettimeofday(&t, NULL);
   return t.tv_sec*1000 + t.tv_usec/1000;
 }
 
--- conga/ricci/common/XML.cpp	2006/10/23 21:13:20	1.6.2.1
+++ conga/ricci/common/XML.cpp	2007/04/12 17:19:31	1.6.2.1.2.1
@@ -189,7 +189,7 @@
 			      xml.size(),
 			      "noname.xml",
 			      NULL,
-			      XML_PARSE_NONET);
+			      XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
   if (!doc)
     throw String("parseXML(): couldn't parse xml");
   
@@ -216,7 +216,7 @@
 			      xml.size(),
 			      "noname.xml",
 			      NULL,
-			      XML_PARSE_NONET);
+			      XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
   if (!doc) {
     //    cout << xml << endl;
     throw String("generateXML(): internal error");
--- conga/ricci/common/executils.cpp	2006/08/12 00:38:36	1.7
+++ conga/ricci/common/executils.cpp	2007/04/12 17:19:31	1.7.6.1
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2005
+  Copyright Red Hat, Inc. 2005-2007
 
   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
@@ -26,6 +26,7 @@
 #include "Time.h"
 
 #include <unistd.h>
+#include <stdlib.h>
 #include <sys/poll.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -110,7 +111,10 @@
     sigset_t set;
     sigfillset(&set);
     sigprocmask(SIG_UNBLOCK, &set, NULL);
-    
+
+    setenv("LANG", "C", 1);
+    setenv("LC_ALL", "C", 1);
+
     /* exec */
     
     try {
--- conga/ricci/docs/cluster_api.html	2006/10/05 17:38:01	1.4
+++ conga/ricci/docs/cluster_api.html	2007/04/12 17:19:31	1.4.6.1
@@ -8,7 +8,7 @@
 	<META NAME="CHANGED" CONTENT="20060620;15340700">
 </HEAD>
 <BODY LANG="en-US" DIR="LTR">
-<P>Cluster module manages Redhat Cluster Suite. 
+<P>Cluster module manages Red Hat Cluster Suite. 
 </P>
 <P>Module name: “cluster” 
 </P>
@@ -75,15 +75,27 @@
 		<P>Start service “servicename”. If “nodename” is specified,
 		service is started on that node, otherwise it will be started on a
 		node chosen by service manager. <BR>If service was not running, it
-		is started. If it was running, it is migrated to “nodename”. 
+		is started. If it was running, it is relocated to “nodename”. 
 		</P>
 		<P>Input variables:<BR>- “servicename” (string) – name of
 		service to manipulate<BR>- “nodename” (string) – optional
-		name of node for service to start on/ migrate to</P>
+		name of node for service to start on / relocate to</P>
 		<P>No output variables.</P>
 		<P>On failure:<BR>- 1 – service manager is not running on this
 		managed system<BR>- generic ones</P>
 	</UL>
+	<LI><P>migrate_service</P>
+	<UL>
+		<P>Migrate the virtual service “servicename” to cluster node “nodename.” To migrate the virtual service, it must be started, and xend must be running and configured to allow live migration on both its current node and on “nodename.” If the service is not started, it will be started on “nodename.”
+		</P>
+		<P>Input variables:<BR>- “servicename” (string) – name of
+		the virtual service to migrate.<BR>- “nodename” (string) – 
+		name of node to which the virtual service is to be migrated.</P>
+		<P>No output variables.</P>
+		<P>On failure:<BR>- 1 – service manager is not running on this
+		managed system<BR>- generic ones</P>
+	</UL>
+
 	<LI><P>stop_service</P>
 	<UL>
 		<P>Stop service “servicename”. It is not an error to stop
--- conga/ricci/docs/modules.html	2006/06/05 19:54:40	1.4
+++ conga/ricci/docs/modules.html	2007/04/12 17:19:31	1.4.6.1
@@ -14,7 +14,7 @@
 <P>Management Modules:</P>
 <UL>
 	<LI><P><A HREF="cluster_api.html">Cluster Module</A> – Manages
-	Redhat Cluster Suite</P>
+	Red Hat Cluster Suite</P>
 	<LI><P><A HREF="rpm_api.html">Rpm Module</A> – Manages rpm
 	packages. Allows retrieval of currently installed rpms, querying
 	repositories, and installation/upgrade of rpms using repositories
@@ -35,4 +35,4 @@
 <P><BR><BR>
 </P>
 </BODY>
-</HTML>
\ No newline at end of file
+</HTML>
--- conga/ricci/docs/rpm_api.html	2006/10/12 19:13:11	1.2
+++ conga/ricci/docs/rpm_api.html	2007/04/12 17:19:31	1.2.6.1
@@ -36,10 +36,10 @@
 installed; and upgraded, if already installed. 
 </P>
 <P>There are couple of predefined rpm sets: <BR>- “Cluster Base”
-- base infrastructure of Redhat Cluster Suite (currently ccs, cman,
+- base infrastructure of Red Hat Cluster Suite (currently ccs, cman,
 dlm, fence, and respective kernel-... rpms) <BR>- “Cluster Base -
-Gulm” - base infrastructure of Redhat Cluster Suite using Gulm lock
-manager (currently ccs, gulm, fence and respective kernel-... rpms)
+Gulm” - base infrastructure of Red Hat Cluster Suite using GULM lock
+manager (currently ccs, gulm, and respective kernel-... rpms)
 <BR>- “Cluster Service Manager” - (currently rgmanager, magma,
 magma-plugins) <BR>- “Clustered Storage” - shared storage
 (currently GFS, lvm2-cluster and respective kernel-... rpms) <BR>-
--- conga/ricci/docs/service_api.html	2006/04/12 15:47:09	1.1
+++ conga/ricci/docs/service_api.html	2007/04/12 17:19:31	1.1.6.1
@@ -30,10 +30,10 @@
 running="true/false"/><BR>“enabled” - enabled on
 boot; “running” - currently running.</P>
 <P>There are couple of predefined service sets: <BR>- “Cluster
-Base” - base infrastructure of Redhat Cluster Suite (currently
+Base” - base infrastructure of Red Hat Cluster Suite (currently
 ccsd, cman, fenced) <BR>- “Cluster Base - Gulm” - base
-infrastructure of Redhat Cluster Suite using Gulm lock manager
-(currently ccsd, lock_gulmd, fenced) <BR>- “Cluster Service
+infrastructure of Red Hat Cluster Suite using GULM lock manager
+(currently ccsd, lock_gulmd) <BR>- “Cluster Service
 Manager” - (currently rgmanager) <BR>- “Clustered Storage” -
 shared storage (currently clvmd, gfs)<BR>- “Linux Virtual Server”
 - (currently pulse, piranha-gui)</P>
@@ -117,6 +117,18 @@
 		generic ones might get returned. 
 		</P>
 	</UL>
+	<LI><P>restart</P>
+	<UL>
+		<P ALIGN=LEFT>Restart services/sets.</P>
+		<P>Input variables: <BR>- “services” (list_xml) – list of
+		services/sets to restart. For format see “query”. 
+		</P>
+		<P>No output variables. 
+		</P>
+		<P ALIGN=LEFT>On failure: <BR>- No special errors defined, only
+		generic ones might get returned. 
+		</P>
+	</UL>
 	<LI><P>stop</P>
 	<UL>
 		<P ALIGN=LEFT>Stop services/sets. It is not error to stop already
@@ -135,4 +147,4 @@
 <P ALIGN=LEFT><BR><BR>
 </P>
 </BODY>
-</HTML>
\ No newline at end of file
+</HTML>
--- conga/ricci/docs/storage_api.html	2006/12/12 13:26:24	1.5.2.1
+++ conga/ricci/docs/storage_api.html	2007/04/12 17:19:31	1.5.2.1.2.1
@@ -78,6 +78,13 @@
 	operations to hang until quorum is regained. User should make
 	cluster quorate before continuing. 
 	</P>
+	<LI><P>6 – cluster locking not enabled<BR>LVM is not configured to use cluster
+	locking mechanism, but some volumes are marked clustered. 
+	User should enable cluster locking. 
+	</P>
+	<LI><P>7 – cluster not running on node<BR>LVM is configured to use cluster
+	locking mechanism, but cluster is not running on local node. 
+	</P>
 </UL>
 <P><BR><BR>
 </P>
/cvs/cluster/conga/ricci/include/Network.h,v  -->  standard output
revision 1.1.6.1
--- conga/ricci/include/Network.h
+++ -	2007-04-12 18:19:53.491782000 +0100
@@ -0,0 +1,54 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/*
+ * Author: Stanko Kupcevic <kupcevic at redhat.com>
+ */
+
+
+#ifndef Conga_Network_h
+#define Conga_Network_h
+
+#include "counting_auto_ptr.h"
+#include "String.h"
+#include <vector>
+
+#include <netdb.h>
+
+
+class Network
+{
+ public:
+  static std::vector<String> name2IP(const String& hostname);
+  static String localhost();
+  
+  
+  class Hostent
+  {
+   public:
+    struct hostent ent;
+    char data[4*1024 - sizeof(struct hostent)];
+    struct hostent* operator->() { return &ent; }
+    struct hostent& operator*() { return ent; }
+  };
+  static counting_auto_ptr<Hostent> getHostByName(const String& hostname);
+  
+};
+
+
+#endif  // Conga_Network_h
--- conga/ricci/include/Socket.h	2006/08/10 22:53:07	1.3
+++ conga/ricci/include/Socket.h	2007/04/12 17:19:34	1.3.6.1
@@ -34,14 +34,11 @@
 // provide external locking
 
 
-std::vector<String> name2IP(const String& hostname);
-
-
 class Socket
 {
  public:
   Socket(const Socket&);
-  Socket& operator= (const Socket&);
+  virtual Socket& operator= (const Socket&);
   virtual ~Socket();
   
   virtual bool operator== (const Socket&);
@@ -73,24 +70,29 @@
  public:
   ClientSocket();
   ClientSocket(const String& sock_path);  // UNIX socket
-  ClientSocket(const String& hostname, unsigned short port);  // TCP socket
+  ClientSocket(const String& hostname, unsigned short port,
+	       unsigned int timeout_ms=0  /*  0 - standard blocking behavior
+					     >0 - timeout
+					  */ );  // TCP socket
   ClientSocket(const ClientSocket&);
-  ClientSocket& operator= (const ClientSocket&);
+  virtual ClientSocket& operator= (const ClientSocket&);
   virtual ~ClientSocket();
   
-  String recv();
-  String send(const String& msg);  // return what is left to send
+  virtual String recv();
+  virtual String recv(int timeout);
+  virtual String send(const String& msg);  // return what is left to send
+  virtual String send(const String& msg, int timeout);
   
-  void ready(bool& recv, bool& send, int timeout);
+  virtual void ready(bool& recv, bool& send, int timeout);
   
   virtual bool server() { return false; }
   
   virtual bool connected_to(const String& hostname);
   
  protected:
-  unsigned int _addr;
+  u_int32_t _addr;  // address in network byte order 
   
-  ClientSocket(int sock, unsigned int addr=0);  // takes ownership of sock
+  ClientSocket(int sock, u_int32_t addr=0);  // takes ownership of sock
   friend class ServerSocket;
 };  // ClientSocket
 
@@ -101,12 +103,12 @@
   ServerSocket(const String& sock_path); // UNIX socket
   ServerSocket(unsigned short port); // TCP socket
   ServerSocket(const ServerSocket&);
-  ServerSocket& operator= (const ServerSocket&);
+  virtual ServerSocket& operator= (const ServerSocket&);
   virtual ~ServerSocket();
   
   ClientSocket accept();
   
-  bool ready(int timeout);
+  virtual bool ready(int timeout);
   
   virtual bool server() { return true; }
   
--- conga/ricci/modules/cluster/ClusterModule.cpp	2006/11/20 23:15:03	1.5.2.1
+++ conga/ricci/modules/cluster/ClusterModule.cpp	2007/04/12 17:19:34	1.5.2.1.2.1
@@ -36,6 +36,7 @@
 static VarMap set_cluster_conf(const VarMap& args);
 static VarMap cluster_status(const VarMap& args);
 static VarMap service_start(const VarMap& args);
+static VarMap service_migrate(const VarMap& args);
 static VarMap service_stop(const VarMap& args);
 static VarMap service_restart(const VarMap& args);
 static VarMap fence_node(const VarMap& args);
@@ -66,6 +67,7 @@
   api_1_0["start_service"]                        = service_start;
   api_1_0["stop_service"]                         = service_stop;
   api_1_0["restart_service"]                      = service_restart;
+  api_1_0["migrate_service"]                      = service_migrate;
   
   api_1_0["start_node"]                           = start_node;
   api_1_0["stop_node"]                            = stop_node;
@@ -168,6 +170,29 @@
 }
 
 VarMap 
+service_migrate(const VarMap& args)
+{
+  String service_name, node_name;
+  try {
+    VarMap::const_iterator iter = args.find("servicename");
+    if (iter == args.end())
+      throw APIerror("missing servicename variable");
+    service_name = iter->second.get_string();
+    
+    iter = args.find("nodename");
+    if (iter != args.end())
+      node_name = iter->second.get_string();
+  } catch ( String e ) {
+    throw APIerror(e);
+  }
+  
+  Clusvcadm::migrate(service_name, node_name);
+  
+  VarMap ret;
+  return ret;
+}
+
+VarMap 
 service_restart(const VarMap& args)
 {
   String name;
--- conga/ricci/modules/cluster/ClusterStatus.cpp	2006/10/24 14:31:40	1.15.2.1
+++ conga/ricci/modules/cluster/ClusterStatus.cpp	2007/04/12 17:19:34	1.15.2.1.2.1
@@ -42,6 +42,7 @@
 #define LSMOD_PATH            "/sbin/lsmod"
 #define MODPROBE_PATH         "/sbin/modprobe"
 #define CHKCONFIG_PATH        "/sbin/chkconfig"
+#define LVMCONF_PATH          "/usr/sbin/lvmconf"
 
 #define CMAN_LEAVE_TIMEOUT    "120"  // seconds (string)
 #define CLUMON_SYNC_TIME      8      // seconds
@@ -157,10 +158,11 @@
 {
   XMLObject cluster_conf(ClusterConf::get()); // bailout if cluster.conf not present
   XMLObject stat = status();
+  bool cman_cluster = ClusterConf::is_cman(cluster_conf);
   
   if (stat.get_attr("cluster_version") == "4") {
     run_initd("ccsd", true, false);
-    if (ClusterConf::is_cman(cluster_conf))
+    if (cman_cluster)
       try {
 	run_initd("cman", true, true);
       } catch ( ... ) {
@@ -197,7 +199,8 @@
     
     if (use_qdisk)
       run_initd("qdiskd", true, false);
-    run_initd("fenced", true, false);
+    if (cman_cluster)
+      run_initd("fenced", true, false);
     run_initd("clvmd", true, false);
     run_initd("gfs", true, false);
     run_initd("rgmanager", true, true);
@@ -205,18 +208,19 @@
     // enable them on boot
     
     run_chkconfig("ccsd", true);
-    if (ClusterConf::is_cman(cluster_conf)) {
+    if (cman_cluster) {
       run_chkconfig("cman", true);
       run_chkconfig("lock_gulmd", false);
+      run_chkconfig("fenced", true);
     } else {
       run_chkconfig("cman", false);
+      run_chkconfig("fenced", false);
       run_chkconfig("lock_gulmd", true);
     }
     if (use_qdisk)
       run_chkconfig("qdiskd", true);
     else
       run_chkconfig("qdiskd", false);
-    run_chkconfig("fenced", true);
     run_chkconfig("clvmd", true);
     run_chkconfig("gfs", true);
     run_chkconfig("rgmanager", true);
@@ -343,6 +347,15 @@
   
   if (purge_conf) {
     ClusterConf::purge_conf();
+    
+    // disable LVM cluster locking
+    try {
+      String out, err;
+      int status;
+      vector<String> args;
+      args.push_back("--disable-cluster");
+      utils::execute(LVMCONF_PATH, args, out, err, status, false);
+    } catch ( ... ) {}
   }
 }
 
--- conga/ricci/modules/cluster/Clusvcadm.cpp	2006/10/24 14:31:40	1.7.2.1
+++ conga/ricci/modules/cluster/Clusvcadm.cpp	2007/04/12 17:19:34	1.7.2.1.2.1
@@ -47,7 +47,8 @@
 	      RG_STATE_CHECK           = 116,    // Checking status
 	      RG_STATE_ERROR           = 117,    // Recoverable error
 	      RG_STATE_RECOVER         = 118,    // Pending recovery
-	      RG_STATE_DISABLED        = 119};   // Resource not allowd to run
+	      RG_STATE_DISABLED        = 119,    // Resource not allowd to run
+	      RG_STATE_MIGRATE         = 120};   // Resource migrating
   
   ServiceStatus(const String& name,
 		const String& node, 
@@ -72,7 +73,6 @@
 
 
 
-
 void 
 Clusvcadm::start(const String& servicename,
 		 const String& nodename)
@@ -97,6 +97,10 @@
        iter++)
     if (iter->name == servicename) {
       String flag;
+
+      if (iter->status == ServiceStatus::RG_STATE_MIGRATE)
+         throw String(servicename + " is in the process of being migrated");
+		
       if (iter->status == ServiceStatus::RG_STATE_STOPPED ||
 	  iter->status == ServiceStatus::RG_STATE_STOPPING ||
 	  iter->status == ServiceStatus::RG_STATE_FAILED ||
@@ -104,19 +108,76 @@
 	  iter->status == ServiceStatus::RG_STATE_DISABLED)
 	flag = "-e";
       else if (iter->status == ServiceStatus::RG_STATE_STARTED ||
-	       iter->status == ServiceStatus::RG_STATE_STARTING) {
+	       iter->status == ServiceStatus::RG_STATE_STARTING)
+	flag = "-r";
+      
+      if (flag.size()) {
+	String out, err;
+	int status;
+	vector<String> args;
+	args.push_back(flag);
 	if (iter->vm)
-	  flag = "-M";
+		args.push_back("vm:" + servicename);
 	else
-	  flag = "-r";
+		args.push_back(servicename);
+	if (nodename.size()) {
+	  args.push_back("-m");
+	  args.push_back(nodename);
+	}
+	if (utils::execute(CLUSVCADM_TOOL_PATH, args, out, err, status, false))
+	  throw command_not_found_error_msg(CLUSVCADM_TOOL_PATH);
+	if (status != 0)
+	  throw String("clusvcadm failed");
       }
+      return;
+    }
+  
+  throw String("no such service");
+}
+
+void 
+Clusvcadm::migrate(const String& servicename, const String& nodename)
+{
+  pair<list<String>, list<ServiceStatus> > info = service_states();
+  list<String> nodes = info.first;
+  list<ServiceStatus> services = info.second;
+  
+  // check if node can run services
+  bool node_found = false;
+  for (list<String>::const_iterator iter = nodes.begin();
+       iter != nodes.end();
+       iter++)
+    if (*iter == nodename)
+      node_found = true;
+  if (!node_found && nodename.size())
+    throw String("node unable to run services");
+  
+  // start
+  for (list<ServiceStatus>::const_iterator iter = services.begin();
+       iter != services.end();
+       iter++) {
+	if (!iter->vm)
+		continue;
+    if (iter->name == servicename) {
+      String flag;
+      if (iter->status == ServiceStatus::RG_STATE_MIGRATE)
+         throw String(servicename + " is already in the process of being migrated");
+      if (iter->status == ServiceStatus::RG_STATE_STOPPED ||
+	  iter->status == ServiceStatus::RG_STATE_STOPPING ||
+	  iter->status == ServiceStatus::RG_STATE_FAILED ||
+	  iter->status == ServiceStatus::RG_STATE_ERROR ||
+	  iter->status == ServiceStatus::RG_STATE_DISABLED)
+	flag = "-e";
+      else if (iter->status == ServiceStatus::RG_STATE_STARTED ||
+	       iter->status == ServiceStatus::RG_STATE_STARTING)
+	flag = "-M";
       
       if (flag.size()) {
 	String out, err;
 	int status;
 	vector<String> args;
 	args.push_back(flag);
-	args.push_back(servicename);
+	args.push_back("vm:" + servicename);
 	if (nodename.size()) {
 	  args.push_back("-m");
 	  args.push_back(nodename);
@@ -128,8 +189,9 @@
       }
       return;
     }
+  }
   
-  throw String("no such service");
+  throw String("no such virtual service");
 }
 
 void 
@@ -149,7 +211,10 @@
 	int status;
 	vector<String> args;
 	args.push_back("-d");
-	args.push_back(servicename);
+	if (iter->vm)
+		args.push_back("vm:" + servicename);
+	else
+		args.push_back(servicename);
 	if (utils::execute(CLUSVCADM_TOOL_PATH, args, out, err, status, false))
 	  throw command_not_found_error_msg(CLUSVCADM_TOOL_PATH);
 	if (status != 0)
@@ -173,6 +238,11 @@
        iter++)
     if (iter->name == servicename) {
       String flag;
+      if (iter->status == ServiceStatus::RG_STATE_MIGRATE)
+         throw String(servicename + " is in the process of being migrated");
+      if (iter->status == ServiceStatus::RG_STATE_STARTING)
+         throw String(servicename + " is in the process of being started");
+
       if (iter->status == ServiceStatus::RG_STATE_STOPPED ||
 	  iter->status == ServiceStatus::RG_STATE_STOPPING ||
 	  iter->status == ServiceStatus::RG_STATE_FAILED ||
@@ -187,7 +257,10 @@
 	int status;
 	vector<String> args;
 	args.push_back(flag);
-	args.push_back(servicename);
+	if (iter->vm)
+		args.push_back("vm:" + servicename);
+	else
+		args.push_back(servicename);
 	if (utils::execute(CLUSVCADM_TOOL_PATH, args, out, err, status, false))
 	  throw command_not_found_error_msg(CLUSVCADM_TOOL_PATH);
 	if (status != 0)
--- conga/ricci/modules/cluster/Clusvcadm.h	2006/08/10 22:53:08	1.2
+++ conga/ricci/modules/cluster/Clusvcadm.h	2007/04/12 17:19:34	1.2.6.1
@@ -30,11 +30,10 @@
 class Clusvcadm
 {
  public:
-  static void start(const String& servicename,
-		    const String& nodename);
-  static void stop(const String& servicename);
+  static void start(const String& servicename, const String& nodename);
+  static void migrate(const String& servicename, const String& nodename);
   static void restart(const String& servicename);
-  
+  static void stop(const String& servicename);
 };
 
 
--- conga/ricci/modules/cluster/clumon/src/daemon/Monitor.cpp	2006/12/13 19:21:52	1.10.2.3
+++ conga/ricci/modules/cluster/clumon/src/daemon/Monitor.cpp	2007/04/12 17:19:34	1.10.2.3.2.1
@@ -26,6 +26,7 @@
 #include "Logger.h"
 #include "Time.h"
 #include "utils.h"
+#include "Network.h"
 
 #include <sys/poll.h>
 #include <sys/sysinfo.h>
@@ -584,7 +585,7 @@
        iter != nodenames.end();
        iter++) {
     const String& nodename = *iter;
-    vector<String> ips = name2IP(nodename);
+    vector<String> ips = Network::name2IP(nodename);
     for (vector<String>::iterator iter_ip = ips.begin();
 	 iter_ip != ips.end();
 	 iter_ip++) {
--- conga/ricci/modules/cluster/clumon/src/daemon/Monitor.h	2006/10/13 09:36:16	1.5
+++ conga/ricci/modules/cluster/clumon/src/daemon/Monitor.h	2007/04/12 17:19:34	1.5.6.1
@@ -49,7 +49,7 @@
 			   const String& msg);
   
  protected:
-  void run();
+  virtual void run();
  private:
   
   Mutex _mutex; // _cluster and _cache
--- conga/ricci/modules/log/LogParser.cpp	2006/11/16 19:34:53	1.6.2.2
+++ conga/ricci/modules/log/LogParser.cpp	2007/04/12 17:19:35	1.6.2.2.2.1
@@ -43,6 +43,11 @@
 				"modclusterd",
 				"dlm",
 				"gulm",
+				"lock_gulmd",
+				"lock_gulmd_main",
+				"lock_gulmd_core",
+				"lock_gulmd_LT",
+				"lock_gulmd_LTPX",
 				"cman",
 				"cman_tool",
 				"ccs",
--- conga/ricci/modules/rpm/PackageHandler.cpp	2006/12/13 20:27:40	1.9.2.2
+++ conga/ricci/modules/rpm/PackageHandler.cpp	2007/04/12 17:19:35	1.9.2.2.2.1
@@ -26,6 +26,7 @@
 #include "File.h"
 
 #include <unistd.h>
+#include <sys/utsname.h>
 
 
 using namespace std;
@@ -132,7 +133,7 @@
       line = utils::strip(line);
       vector<String> words = utils::split(line);
       vector<String>::size_type l = words.size();
-      if (l != 3)
+      if (l < 3)
 	continue;
       String name = words[0];
       String::size_type idx = name.rfind('.');
@@ -154,10 +155,6 @@
     return true;
   
   if (RHEL4) {
-    
-    // TODO: fix up2date handling
-    throw String("RHEL4 not supported, yet");
-    
     String out, err;
     int status;
     vector<String> args;
@@ -416,7 +413,9 @@
     set.packages.push_back("dlm");
     set.packages.push_back("fence");
     
-    String kernel(utils::strip(File::open("/proc/sys/kernel/osrelease")));
+    struct utsname uts;
+    uname(&uts);
+    String kernel(uts.release);
     if (kernel.find("smp") != kernel.npos) {
       set.packages.push_back("cman-kernel-smp");
       set.packages.push_back("dlm-kernel-smp");
@@ -442,7 +441,6 @@
   if (RHEL4 || FC5) {
     set.packages.push_back("ccs");
     set.packages.push_back("gulm");
-    set.packages.push_back("fence");
   } else
     throw String("GULM not available in CS5");
   
@@ -464,7 +462,9 @@
 PackageSet
 PackageHandler::build_cluster_storage_set()
 {
-  String kernel(utils::strip(File::open("/proc/sys/kernel/osrelease")));
+  struct utsname uts;
+  uname(&uts);
+  String kernel(uts.release);
   
   PackageSet set("Clustered Storage");
   set.packages.push_back("lvm2-cluster");
@@ -523,10 +523,9 @@
       const Package& pack = iter->second;
       if (pack.version.empty())
 	set.installed = false;
-      if (pack.repo_version.empty()) {
+      if (pack.repo_version.empty())
 	set.in_repo = false;
-	set.upgradeable = false;
-      } else if (pack.repo_version > pack.version)
+      else if (pack.repo_version > pack.version)
 	set.upgradeable = true;
     }
   }
--- conga/ricci/modules/service/ServiceManager.cpp	2006/10/23 21:13:21	1.5.2.1
+++ conga/ricci/modules/service/ServiceManager.cpp	2007/04/12 17:19:36	1.5.2.1.2.1
@@ -172,7 +172,15 @@
 Service::start()
 {
   running();
-  run_service(name(), true);
+  run_service(name(), START);
+  *_running = true;
+}
+
+void 
+Service::restart()
+{
+  running();
+  run_service(name(), RESTART);
   *_running = true;
 }
 
@@ -180,7 +188,7 @@
 Service::stop()
 {
   running();
-  run_service(name(), false);
+  run_service(name(), STOP);
   *_running = false;
 }
 
@@ -217,7 +225,7 @@
 }
 
 void 
-Service::run_service(const String& name, bool on)
+Service::run_service(const String& name, ActionState state)
 {
   String path(INITD_DIR_PATH);
   path += name;
@@ -225,20 +233,27 @@
   String out, err;
   int status;
   vector<String> args;
-  if (on)
-    args.push_back("start");
-  else
-    args.push_back("stop");
+  switch (state) {
+    case START:
+      args.push_back("start");
+      break;
+    case STOP:
+      args.push_back("stop");
+      break;
+    case RESTART:
+      args.push_back("restart");
+      break;
+  }
   if (utils::execute(path, args, out, err, status, false) != 0)
     throw command_not_found_error_msg(path);
   if (status) {
     bool running = service_running(name);
-    if (on) {
+    if (state == START || state == RESTART) {
       if (!running)
-	throw String("service ") + name + " " + String(on?"start":"stop") + " failed";
+	throw String("service ") + name + " " + String(state == START ? "start" : "restart") + " failed";
     } else {
       if (running)
-	throw String("service ") + name + " " + String(on?"start":"stop") + " failed";
+	throw String("service ") + name + " stop failed";
     }
   }
 }
@@ -363,6 +378,19 @@
 }
 
 void 
+ServiceSet::restart()
+{
+  name();
+  try {
+    // ordered sequence: last started, first to be stoped
+    stop();
+    start();
+  } catch (String e) {
+    throw String("service set '") + name() + "' failed to restart";
+  }
+}
+
+void 
 ServiceSet::stop()
 {
   name();
@@ -440,11 +468,10 @@
   if (RHEL4 || FC5) {
     servs.clear();
     name = "Cluster Base - Gulm";
-    descr = "Cluster infrastructure: ccs, gulm, fence";
+    descr = "Cluster infrastructure: ccs, gulm";
     s = ServiceSet(name, descr);
     servs.push_back("ccsd");
     servs.push_back("lock_gulmd");
-    servs.push_back("fenced");
     if (populate_set(s, servs))
       sets[name] = s;
   }
@@ -590,6 +617,34 @@
 }
 
 void 
+ServiceManager::restart(const std::list<String>& services, 
+		     const std::list<String>& sets)
+{
+  // check
+  for (list<String>::const_iterator iter = services.begin();
+       iter != services.end();
+       iter++)
+    if (_servs.find(*iter) == _servs.end())
+      throw String("no such service: ") + *iter;
+  for (list<String>::const_iterator iter = sets.begin();
+       iter != sets.end();
+       iter++)
+    if (_sets.find(*iter) == _sets.end())
+      throw String("no such service set: ") + *iter;
+  
+  // apply
+  for (list<String>::const_iterator iter = services.begin();
+       iter != services.end();
+       iter++)
+    _servs[*iter].restart();
+  
+  for (list<String>::const_iterator iter = sets.begin();
+       iter != sets.end();
+       iter++)
+    _sets[*iter].restart();
+}
+
+void 
 ServiceManager::stop(const std::list<String>& services, 
 		     const std::list<String>& sets)
 {
--- conga/ricci/modules/service/ServiceManager.h	2006/08/10 22:53:09	1.2
+++ conga/ricci/modules/service/ServiceManager.h	2007/04/12 17:19:36	1.2.6.1
@@ -48,6 +48,7 @@
   
   void enable();
   void disable();
+  void restart();
   void start();
   void stop();
   
@@ -61,9 +62,15 @@
   mutable counting_auto_ptr<bool> _enabled;
   mutable counting_auto_ptr<bool> _running;
   
+  enum ActionState {
+    START,
+    STOP,
+    RESTART
+  };
+  
   static void enable_service(const String& name, bool on);
   static bool service_running(const String& name);
-  static void run_service(const String& name, bool on);
+  static void run_service(const String& name, ActionState state);
   
   friend class ServiceManager;
 
@@ -84,6 +91,7 @@
   void enable();
   void disable();
   void start();
+  void restart();
   void stop();
   
   std::list<Service> servs;
@@ -107,6 +115,7 @@
   void disable(const std::list<String>& services, const std::list<String>& sets);
   
   void start(const std::list<String>& services, const std::list<String>& sets);
+  void restart(const std::list<String>& services, const std::list<String>& sets);
   void stop(const std::list<String>& services, const std::list<String>& sets);
   
   void lists(std::list<Service>& services, 
--- conga/ricci/modules/service/ServiceModule.cpp	2006/08/10 22:53:09	1.3
+++ conga/ricci/modules/service/ServiceModule.cpp	2007/04/12 17:19:36	1.3.6.1
@@ -31,6 +31,7 @@
 static VarMap enable(const VarMap& args);
 static VarMap disable(const VarMap& args);
 static VarMap start(const VarMap& args);
+static VarMap restart(const VarMap& args);
 static VarMap stop(const VarMap& args);
 static VarMap lists(const VarMap& args);
 static VarMap query(const VarMap& args);
@@ -53,6 +54,7 @@
   api_1_0["enable"]     = enable;
   api_1_0["disable"]    = disable;
   api_1_0["start"]      = start;
+  api_1_0["restart"]	= restart;
   api_1_0["stop"]       = stop;
   api_1_0["list"]       = lists;
   api_1_0["query"]      = query;
@@ -149,6 +151,34 @@
 }
 
 VarMap 
+restart(const VarMap& args)
+{
+  list<XMLObject> serv_list;
+  try {
+    VarMap::const_iterator iter = args.find("services");
+    if (iter == args.end())
+      throw APIerror("missing services variable");
+    serv_list = iter->second.get_list_XML();
+  } catch ( String e ) {
+    throw APIerror(e);
+  }
+  
+  list<String> services, sets;
+  for (list<XMLObject>::const_iterator iter = serv_list.begin();
+       iter != serv_list.end();
+       iter++) {
+    if (iter->tag() == "service")
+      services.push_back(iter->get_attr("name"));
+    else if (iter->tag() == "set")
+      sets.push_back(iter->get_attr("name"));
+  }
+  
+  ServiceManager().restart(services, sets);
+  
+  return VarMap();
+}
+
+VarMap 
 stop(const VarMap& args)
 {
   list<XMLObject> serv_list;
/cvs/cluster/conga/ricci/modules/storage/ClusterNotRunningError.h,v  -->  standard output
revision 1.2.2.1
--- conga/ricci/modules/storage/ClusterNotRunningError.h
+++ -	2007-04-12 18:19:54.743024000 +0100
@@ -0,0 +1,41 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/*
+ * Author: Stanko Kupcevic <kupcevic at redhat.com>
+ */
+
+
+#ifndef ClusterNotRunningError_h
+#define ClusterNotRunningError_h
+
+#include "Except.h"
+
+
+class ClusterNotRunningError : public Except
+{
+ public:
+  ClusterNotRunningError()
+    : Except(7, String("Cluster infrastructure is not active")) {}
+  virtual ~ClusterNotRunningError()
+    {}
+  
+};
+
+
+#endif  // ClusterNotRunningError_h
/cvs/cluster/conga/ricci/modules/storage/LVMClusterLockingError.h,v  -->  standard output
revision 1.2.2.1
--- conga/ricci/modules/storage/LVMClusterLockingError.h
+++ -	2007-04-12 18:19:54.825534000 +0100
@@ -0,0 +1,41 @@
+/*
+  Copyright Red Hat, Inc. 2007
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by the
+  Free Software Foundation; either version 2, or (at your option) any
+  later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
+  MA 02139, USA.
+*/
+/*
+ * Author: Stanko Kupcevic <kupcevic at redhat.com>
+ */
+
+
+#ifndef LVMClusterLockingError_h
+#define LVMClusterLockingError_h
+
+#include "Except.h"
+
+
+class LVMClusterLockingError : public Except
+{
+ public:
+  LVMClusterLockingError()
+    : Except(6, String("LVM cluster locking is not enabled")) {}
+  virtual ~LVMClusterLockingError()
+    {}
+  
+};
+
+
+#endif  // LVMClusterLockingError_h
--- conga/ricci/modules/storage/ClusterNotQuorateError.h	2006/08/10 22:53:09	1.2
+++ conga/ricci/modules/storage/ClusterNotQuorateError.h	2007/04/12 17:19:36	1.2.6.1
@@ -31,7 +31,7 @@
 {
  public:
   ClusterNotQuorateError()
-    : Except(5, String("Quorum required, but cluster not quorate")) {}
+    : Except(5, String("Cluster is not quorate")) {}
   virtual ~ClusterNotQuorateError()
     {}
   
--- conga/ricci/modules/storage/ClvmdError.h	2006/08/10 22:53:09	1.2
+++ conga/ricci/modules/storage/ClvmdError.h	2007/04/12 17:19:36	1.2.6.1
@@ -31,7 +31,7 @@
 {
  public:
   ClvmdError()
-    : Except(4, String("clvmd required, but unable to start. Start cluster infrastructure.")) {}
+    : Except(4, String("clvmd failed to start")) {}
   virtual ~ClvmdError()
     {}
   
--- conga/ricci/modules/storage/GFS1.cpp	2006/10/06 03:10:13	1.3
+++ conga/ricci/modules/storage/GFS1.cpp	2007/04/12 17:19:36	1.3.6.1
@@ -264,7 +264,7 @@
 		    name,
 		    1,
 		    16,
-		    String(" ;!@#$%^& *()+=/\\|?><,.\"':;"),
+		    NAMES_ILLEGAL_CHARS, 
 		    list<String>());
   name_var.set_conditional_bool_if("clustered");
   _props.set(name_var);
--- conga/ricci/modules/storage/GFS2.cpp	2006/10/06 03:10:13	1.3
+++ conga/ricci/modules/storage/GFS2.cpp	2007/04/12 17:19:36	1.3.6.1
@@ -147,7 +147,6 @@
 create_GFS2(const String& path,
 	    const counting_auto_ptr<ContentTemplate>& templ)
 {
-  String bs = utils::to_string(templ->_props.get("block_size").get_int());
   String jnum = utils::to_string(templ->_props.get("journals_num").get_int());
   long long jsize_bytes = templ->_props.get("journal_size").get_int();
   String jsize = utils::to_string(jsize_bytes / 1024 / 1024);
@@ -165,9 +164,6 @@
   
   
   vector<String> args;
-  args.push_back("-b");
-  args.push_back(bs);
-  
   args.push_back("-J");
   args.push_back(jsize);
   
@@ -208,14 +204,6 @@
   // mountpoints
   mount_props_template(gfs2_module, _props);
   
-  // block_size
-  list<long long> b_sizes;
-  b_sizes.push_back(512);
-  b_sizes.push_back(1024);
-  b_sizes.push_back(2048);
-  b_sizes.push_back(4096);
-  _props.set(Variable("block_size", 4096, b_sizes));
-  
   // journals
   _props.set(Variable("journals_num", 1, 1, 128, 1));
   list<long long> jsizes;
@@ -264,7 +252,7 @@
 		    name,
 		    1,
 		    16,
-		    String(" ;!@#$%^&*()+=/\\|?><,.\"':;"),
+		    NAMES_ILLEGAL_CHARS, 
 		    list<String>());
   name_var.set_conditional_bool_if("clustered");
   _props.set(name_var);
--- conga/ricci/modules/storage/LV.cpp	2006/12/12 13:26:24	1.6.2.1
+++ conga/ricci/modules/storage/LV.cpp	2007/04/12 17:19:36	1.6.2.1.2.1
@@ -29,7 +29,7 @@
 #include "utils.h"
 #include "ContentFactory.h"
 #include "ContentNone.h"
-#include "ClvmdError.h"
+#include "LVMClusterLockingError.h"
 
 
 using namespace std;
@@ -47,7 +47,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (!LVM::clustered_enabled() &&
       bd_temp.props.get("clustered").get_bool())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   
   String lvname = bd_temp.props.get("lvname").get_string();
@@ -184,7 +184,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (_props.get("clustered").get_bool() &&
       !LVM::clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   // snapshots neither resize nor replace content, see LV()
   
@@ -248,7 +248,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (_props.get("clustered").get_bool() &&
       !LVM::clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   content->remove();
   LVM::lvremove(path());
--- conga/ricci/modules/storage/LVM.cpp	2007/01/17 14:41:13	1.7.2.4
+++ conga/ricci/modules/storage/LVM.cpp	2007/04/12 17:19:36	1.7.2.4.2.1
@@ -29,6 +29,8 @@
 #include "defines.h"
 #include "ClvmdError.h"
 #include "ClusterNotQuorateError.h"
+#include "ClusterNotRunningError.h"
+#include "LVMClusterLockingError.h"
 
 #include <vector>
 
@@ -162,8 +164,11 @@
   int status;
   if (utils::execute(LVM_BIN_PATH, args, out, err, status))
     throw command_not_found_error_msg(LVM_BIN_PATH);
-  if (status != 0)
+  if (status != 0) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     throw String("lvdisplay failed");
+  }
   
   vector<String> lines = utils::split(out, "\n");
   for (vector<String>::iterator iter = lines.begin();
@@ -213,8 +218,11 @@
   int status;
   if (utils::execute(LVM_BIN_PATH, args, out, err, status))
     throw command_not_found_error_msg(LVM_BIN_PATH);
-  if (status != 0)
+  if (status != 0) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     throw String("lvs failed");
+  }
   
   String line = utils::strip(out);
   vector<String> words = utils::split(line, ";");
@@ -268,8 +276,11 @@
   args.push_back("lv_name,vg_name,origin");
   if (utils::execute(LVM_BIN_PATH, args, out, err, status))
     throw command_not_found_error_msg(LVM_BIN_PATH);
-  if (status != 0)
+  if (status != 0) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     throw String("lvs failed");
+  }
   vector<String> lines = utils::split(out, "\n");
   for (vector<String>::iterator iter = lines.begin();
        iter != lines.end();
@@ -381,8 +392,11 @@
   args.push_back("-c");
   if (utils::execute(LVM_BIN_PATH, args, out, err, status))
     throw command_not_found_error_msg(LVM_BIN_PATH);
-  if (status != 0)
+  if (status != 0) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     throw String("lvdisplay failed");
+  }
   vector<String> lines = utils::split(out, "\n");
   for (vector<String>::iterator iter = lines.begin();
        iter != lines.end();
@@ -441,7 +455,7 @@
 {
   if (clustered &&
       !clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   vector<String> args;
   args.push_back("vgcreate");
@@ -524,7 +538,7 @@
 {
   if (clustered &&
       !clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   vector<String> args;
   args.push_back("vgchange");
@@ -701,6 +715,8 @@
 bool
 cluster_quorate()
 {
+  // called only if cluster locking is enabled
+  
   bool use_magma = true;
   if (access("/sbin/magma_tool", X_OK)) 
     use_magma = false;
@@ -714,11 +730,8 @@
     if (utils::execute("/sbin/magma_tool", args, out, err, status))
       throw command_not_found_error_msg("magma_tool");
     if (status)
-      throw String("cluster tools: magma_tool errored");
-    if (out.find("Quorate") != out.npos)
-      return true;
-    else
-      return false;
+      throw ClusterNotRunningError();
+    return out.find("Quorate") != out.npos;
   } else {
     // use cman_tool
     String cman_tool_path = "/sbin/cman_tool";
@@ -732,7 +745,7 @@
     if (utils::execute(cman_tool_path, args, out, err, status))
       throw command_not_found_error_msg("cman_tool");
     if (status)
-      throw String("cluster tools: cman_tool errored");
+      throw ClusterNotRunningError();
     
     long long quorum = -1;
     long long votes = -1;
@@ -756,7 +769,7 @@
     
     if (quorum <= 0 ||
 	votes < 0)
-      throw String("Unable to retrieve cluster quorum info");
+      throw String("Unable to determine cluster quorum status");
     return votes >= quorum;
   }
 }
@@ -824,8 +837,11 @@
   //    args.push_back(vgname);
   if (utils::execute(LVM_BIN_PATH, args, out, err, status))
     throw command_not_found_error_msg(LVM_BIN_PATH);
-  if (status)
+  if (status) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     throw String("vgs failed");
+  }
   
   vector<String> lines = utils::split(out, "\n");
   for (vector<String>::iterator iter = lines.begin();
@@ -873,11 +889,14 @@
     throw command_not_found_error_msg(LVM_BIN_PATH);
   
   bool use_pvdisplay = false;
-  if (status)
+  if (status) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     // wouldn't `pvdisplay -c` fail if `pvs` has already failed?
     // `pvs` fails if it cannot read one hard drive (common in SANs), 
     // while pvdisplay reports without failure
     use_pvdisplay = true;
+  }
   
   if (use_pvdisplay) {
     args.clear();
@@ -885,8 +904,11 @@
     args.push_back("-c");
     if (utils::execute(LVM_BIN_PATH, args, out, err, status))
       throw command_not_found_error_msg(LVM_BIN_PATH);
-    if (status)
+    if (status) {
+      if (err.find("Skipping clustered") != err.npos)
+	throw LVMClusterLockingError();
       throw String("pvs and pvdisplay failed");
+    }
     
     vector<String> lines = utils::split(utils::strip(out), "\n");
     for (vector<String>::iterator iter = lines.begin();
--- conga/ricci/modules/storage/PV.cpp	2006/12/12 13:26:24	1.4.2.1
+++ conga/ricci/modules/storage/PV.cpp	2007/04/12 17:19:36	1.4.2.1.2.1
@@ -25,7 +25,7 @@
 #include "LVM.h"
 #include "MapperFactory.h"
 #include "utils.h"
-#include "ClvmdError.h"
+#include "LVMClusterLockingError.h"
 
 
 using namespace std;
@@ -96,7 +96,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (!LVM::clustered_enabled() &&
       LVM::vg_clustered(vgname))
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   
   if (vg->sources.size() == 1) {
@@ -124,7 +124,7 @@
   if (!LVM::clustered_enabled() &&
       (LVM::vg_clustered(vgname_old) || 
        LVM::vg_clustered(vgname_new)))
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   
   counting_auto_ptr<Mapper> vg_old = 
@@ -155,7 +155,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (!LVM::clustered_enabled() &&
       LVM::vg_clustered(vgname))
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   
   LVM::pvcreate(path);
--- conga/ricci/modules/storage/VG.cpp	2006/12/12 13:26:24	1.8.2.1
+++ conga/ricci/modules/storage/VG.cpp	2007/04/12 17:19:36	1.8.2.1.2.1
@@ -28,7 +28,7 @@
 #include "defines.h"
 #include "utils.h"
 #include "MidAir.h"
-#include "ClvmdError.h"
+#include "LVMClusterLockingError.h"
 
 #include "Time.h"
 
@@ -55,8 +55,11 @@
   int status;
   if (utils::execute(LVM_BIN_PATH, args, out, err, status))
     throw command_not_found_error_msg(LVM_BIN_PATH);
-  if (status)
+  if (status) {
+    if (err.find("Skipping clustered") != err.npos)
+      throw LVMClusterLockingError();
     throw String("vgs failed");
+  }
   vector<String> lines = utils::split(out, "\n");
   for (vector<String>::iterator iter = lines.begin();
        iter != lines.end();
@@ -103,6 +106,10 @@
       _props.get("extents_total").get_int() * _props.get("extent_size").get_int();
     _props.set(Variable("size", size));
     
+    long long free_size = 
+      _props.get("extents_free").get_int() * _props.get("extent_size").get_int();
+    _props.set(Variable("size_free", free_size));
+    
     bool rem = true;
     for (list<counting_auto_ptr<BD> >::const_iterator iter = targets.begin();
 	 iter != targets.end();
@@ -125,8 +132,6 @@
     
     // ### new targets ###
     
-    long long free_size = 
-      _props.get("extents_free").get_int() * _props.get("extent_size").get_int();
     if (free_size == 0)
       return;
     
@@ -146,11 +151,13 @@
       String lvname = (*iter)->_props.get("lvname").get_string();
       lvnames.push_back(lvname);
     }
+    lvnames.push_back("_mlog");
+    lvnames.push_back("_mimage");
     new_lv->props.set(Variable("lvname", 
 			       String("new_lv"), 
 			       1,
 			       36,
-			       "-?/$%!",
+			       NAMES_ILLEGAL_CHARS, 
 			       lvnames));
     
     new_lv->props.set(Variable("vgname", _vgname));
@@ -165,7 +172,7 @@
 				 String("new_snapshot"), 
 				 1,
 				 36,
-				 "-?/$%!",
+				 NAMES_ILLEGAL_CHARS, 
 				 lvnames));
     
     new_snap->content->_avail_replacements.clear();
@@ -208,7 +215,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (_props.get("clustered").get_bool() &&
       !LVM::clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   String vgname;
   try {
@@ -253,7 +260,7 @@
   // if VG is marked as clustered, but cluster locking is not available, throw
   if (_props.get("clustered").get_bool() &&
       !LVM::clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   String vgname = _props.get("vgname").get_string();
   LVM::vgremove(vgname);
@@ -279,7 +286,7 @@
   
   if (clustered &&
       !LVM::clustered_enabled())
-    throw ClvmdError();
+    throw LVMClusterLockingError();
   
   try {
     utils::clear_cache();
@@ -334,7 +341,7 @@
 		  String("new_vg"), 
 		  1,
 		  36,
-		  "-?/$%#",
+		  NAMES_ILLEGAL_CHARS, 
 		  vgnames);
   props.set(vgname);
   
--- conga/ricci/modules/storage/defines.h	2006/09/26 03:02:57	1.6
+++ conga/ricci/modules/storage/defines.h	2007/04/12 17:19:36	1.6.6.1
@@ -1,5 +1,5 @@
 /*
-  Copyright Red Hat, Inc. 2005
+  Copyright Red Hat, Inc. 2005-2007
 
   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
@@ -100,6 +100,8 @@
 #define FUNC_RESPONSE_TAG   String("function_response")
 
 
+const static String NAMES_ILLEGAL_CHARS("~`!@#$%^&*()+=[]{}|\\:;\"'<>?");
+
 
 
 #endif  // defines_h
--- conga/ricci/modules/storage/parted_wrapper.cpp	2006/10/06 03:10:13	1.8
+++ conga/ricci/modules/storage/parted_wrapper.cpp	2007/04/12 17:19:36	1.8.6.1
@@ -242,6 +242,8 @@
 {
   vector<String> args;
   args.push_back(pt_path);
+  args.push_back("unit");
+  args.push_back("B");
   args.push_back("print");
   args.push_back("-s");
   String out, err;
@@ -594,8 +596,9 @@
   long long bbb;
   list<PartedPartition> parts = plain_partitions(pt_path, aaa, bbb);
   
-  seg_begin = seg_begin / 1024 / 1024;
-  long long seg_end   = seg_begin + size / 1024 / 1024;
+  // parted defines 1KB as 1000 bytes
+  seg_begin = seg_begin / 1000 / 1000;
+  long long seg_end   = seg_begin + size / 1000 / 1000;
   
   vector<String> args;
   args.push_back("-s");
@@ -663,20 +666,21 @@
 {
   String s = utils::to_lower(utils::strip(size_str));
   long long multiplier;
+  // parted defines 1KB as 1000 bytes.
   if (s.find("b") == s.npos)
-    multiplier = 1024 * 1024;  // by old parted behavior, size is in MB
+    multiplier = 1000 * 1000;  // by old parted behavior, size is in MB
   else {
     if (s.size() < 3)
       throw String("parted size has an invalid value: ") + s;
     multiplier = 1;
     if (s[s.size()-2] == 'k')
-      multiplier = 1024;
+      multiplier = 1000;
     else if (s[s.size()-2] == 'm')
-      multiplier = 1024 * 1024;
+      multiplier = 1000 * 1000;
     else if (s[s.size()-2] == 'g')
-      multiplier = 1024 * 1024 * 1024;
+      multiplier = 1000 * 1000 * 1000;
     else if (s[s.size()-2] == 't')
-      multiplier = 1024 * 1024 * 1024 * 1024;
+      multiplier = 1000 * 1000 * 1000 * 1000;
   }
   
   return (long long) utils::to_float(s) * multiplier;
--- conga/ricci/ricci/Ricci.cpp	2007/01/04 00:22:13	1.18.2.4
+++ conga/ricci/ricci/Ricci.cpp	2007/04/12 17:19:36	1.18.2.4.2.1
@@ -31,13 +31,13 @@
 #include "Time.h"
 #include "XML_tags.h"
 #include "File.h"
+#include "Network.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <dirent.h>
-#include <netdb.h>
 
 
 #include <fstream>
@@ -45,7 +45,6 @@
 
 
 static bool dom0();
-static String hostname();
 static pair<String, String> clusterinfo();
 static String os_release();
 
@@ -71,7 +70,7 @@
     header.set_attr("authenticated", "false");
   
   if (full) {
-    String name = hostname();
+    String name = Network::localhost();
     if (name.size())
       header.set_attr("hostname", name);
     
@@ -459,20 +458,6 @@
 
 
 
-
-String 
-hostname()
-{
-  char name[1024];
-  if (gethostname(name, sizeof(name)))
-    return "";
-  struct hostent *ent = gethostbyname(name);
-  if (ent)
-    return String(ent->h_name);
-  else
-    return name;
-}
-
 pair<String, String> 
 clusterinfo()
 {
/cvs/cluster/conga/ricci/test_suite/README,v  -->  standard output
revision 1.2.2.1
--- conga/ricci/test_suite/README
+++ -	2007-04-12 18:19:56.404356000 +0100
@@ -0,0 +1,15 @@
+1. generate SSL cert/key pair
+	./generate_certs.sh
+	this step needs to be performed only once 
+	(cert can be used for multiple riccis - each requires authentication)
+2. modify ricci/authenticate.xml, writing root password of machine hosting ricci to 'password' attribute
+3. execute `./send_to_ricci ricci_hostname ricci/authenticate.xml`
+	from now on, you are authenticated to that ricci, and don't have to repeat authentication to it
+4. pick/modify one of XML files
+	check ../docs/ for API
+5. execute `./send_to_ricci ricci_hostname xml_file`
+	ricci's response will be printed to stdout
+
+...
+
+15. send ricci/unauthenticate.xml to unauthenticate from that ricci
/cvs/cluster/conga/ricci/test_suite/SSLClient_send_to_ricci,v  -->  standard output
revision 1.1.6.1
--- conga/ricci/test_suite/SSLClient_send_to_ricci
+++ -	2007-04-12 18:19:56.492469000 +0100
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+import socket
+import sys, os
+import xml.dom
+import xml
+from xml.dom import minidom
+
+import sys
+sys.path.append('/var/lib/luci/Extensions')
+from conga_ssl import SSLSocket
+
+
+WRITE_TIMEOUT = 600
+READ_TIMEOUT = 600
+CONNECT_TIMEOUT = 4
+
+RICCI_PORT = 11111
+
+
+def send_to_ricci(hostname, msg):
+    ss = SSLSocket(hostname, RICCI_PORT, CONNECT_TIMEOUT)
+    
+    res1 = ss.recv(READ_TIMEOUT)
+    ss.send(msg, WRITE_TIMEOUT)
+    res2 = ''
+    while True:
+        buff = ss.recv(READ_TIMEOUT)
+        if buff == '':
+            break
+        res2 += buff
+        try:
+            minidom.parseString(res2)
+            break
+        except:
+            pass
+    return res1, res2
+
+
+def main(argv):
+    certs_present = True
+    if os.access('cacert.pem', os.R_OK) == False:
+        print 'cannot find cacert.pem'
+        certs_present = False
+    if os.access('privkey.pem', os.R_OK) == False:
+        print 'cannot find privkey.pem'
+        certs_present = False
+    
+    if len(argv) != 3 or not certs_present:
+        print 'sends <command_file> to ricci on <hostname>, and writes its response to stdout'
+        print '\t' + argv[0] + ' <hostname> <command_file>'
+        print '\t\thostname - host to send command to'
+        print '\t\txml_file - file with valid ricci request to be sent'
+        print '\t./ has to contain privkey.pem and cacert.pem'
+        sys.exit(1)
+
+    hostname = argv[1]
+    filename = argv[2]
+    res = send_to_ricci(hostname, open(filename).read(100000))
+    print res[1]
+    if res[1].find('success="5"') > -1:
+        print "not authenticated, send ricci/authenticate.xml with root password in it"
+
+
+
+
+# If called from the command line
+if __name__ == '__main__':
+    main(sys.argv)
+
/cvs/cluster/conga/ricci/test_suite/cacert.config,v  -->  standard output
revision 1.1.6.1
--- conga/ricci/test_suite/cacert.config
+++ -	2007-04-12 18:19:56.578567000 +0100
@@ -0,0 +1,15 @@
+[ req ]
+distinguished_name     = req_distinguished_name
+attributes             = req_attributes
+prompt                 = no
+
+[ req_distinguished_name ]
+C                      = US
+ST                     = State or Province
+L                      = Locality
+O                      = Organization Name
+OU                     = Organizational Unit Name
+CN                     = Common Name
+emailAddress           = root at localhost
+
+[ req_attributes ]
/cvs/cluster/conga/ricci/test_suite/generate_certs.sh,v  -->  standard output
revision 1.2.6.1
--- conga/ricci/test_suite/generate_certs.sh
+++ -	2007-04-12 18:19:56.659048000 +0100
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+/usr/bin/openssl genrsa -out privkey.pem 2048
+/usr/bin/openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1825 -config cacert.config
+chmod go-rwx *.pem
/cvs/cluster/conga/ricci/test_suite/send_to_ricci,v  -->  standard output
revision 1.1.6.1
--- conga/ricci/test_suite/send_to_ricci
+++ -	2007-04-12 18:19:56.738445000 +0100
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+
+import socket
+import sys, os
+import xml.dom
+import xml
+from xml.dom import minidom
+
+
+
+RICCI_PORT = 11111
+
+
+
+def send_to_ricci(hostname, msg):
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.settimeout(2.0)
+    s.connect((hostname, RICCI_PORT))
+    ss = socket.ssl(s, 'privkey.pem', 'cacert.pem')
+    s.settimeout(600.0)
+    
+    res1 = ss.read(1024)
+    ss.write(msg)
+    res2 = ''
+    while True:
+        buff = ss.read(10485760)
+        if buff == '':
+            break
+        res2 += buff
+        try:
+            minidom.parseString(res2)
+            break
+        except:
+            pass
+    return res1, res2
+
+
+def main(argv):
+    certs_present = True
+    if os.access('cacert.pem', os.R_OK) == False:
+        print 'cannot find cacert.pem'
+        certs_present = False
+    if os.access('privkey.pem', os.R_OK) == False:
+        print 'cannot find privkey.pem'
+        certs_present = False
+    
+    if len(argv) != 3 or not certs_present:
+        print 'sends <command_file> to ricci on <hostname>, and writes its response to stdout'
+        print '\t' + argv[0] + ' <hostname> <command_file>'
+        print '\t\thostname - host to send command to'
+        print '\t\txml_file - file with valid ricci request to be sent'
+        print '\t./ has to contain privkey.pem and cacert.pem'
+        sys.exit(1)
+
+    hostname = argv[1]
+    filename = argv[2]
+    res = send_to_ricci(hostname, open(filename).read(100000))
+    print res[1]
+    if res[1].find('success="5"') > -1:
+        print "not authenticated, send ricci/authenticate.xml with root password in it"
+
+
+
+
+# If called from the command line
+if __name__ == '__main__':
+    main(sys.argv)
+
/cvs/cluster/conga/ricci/test_suite/storage/report.xml,v  -->  standard output
revision 1.1.6.1
--- conga/ricci/test_suite/storage/report.xml
+++ -	2007-04-12 18:19:56.825591000 +0100
@@ -0,0 +1,13 @@
+<?xml version="1.0" ?>
+<ricci version="1.0" function="process_batch" async="false">
+<batch>
+
+<module name="storage">
+<request sequence="1254" API_version="1.0">
+<function_call name="report"/>
+</request>
+</module>
+
+</batch>
+</ricci>
+




More information about the Cluster-devel mailing list