[Cluster-devel] conga/luci cluster/form-macros cluster/index_h ...

rmccabe at sourceware.org rmccabe at sourceware.org
Thu Dec 21 05:08:51 UTC 2006


CVSROOT:	/cvs/cluster
Module name:	conga
Changes by:	rmccabe at sourceware.org	2006-12-21 05:08:49

Modified files:
	luci/cluster   : form-macros index_html validate_config_qdisk.js 
	luci/homebase  : form-macros homebase_common.js index_html 
	                 luci_homebase.css validate_sys_remove.js 
	luci/site/luci/Extensions: cluster_adapters.py 
	                           homebase_adapters.py ricci_bridge.py 
	                           ricci_communicator.py 

Log message:
	most of the rest of fixes for bz201394. minor cleanup/polish still forthcoming.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/form-macros.diff?cvsroot=cluster&r1=1.135&r2=1.136
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/index_html.diff?cvsroot=cluster&r1=1.29&r2=1.30
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/cluster/validate_config_qdisk.js.diff?cvsroot=cluster&r1=1.4&r2=1.5
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/form-macros.diff?cvsroot=cluster&r1=1.49&r2=1.50
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/homebase_common.js.diff?cvsroot=cluster&r1=1.14&r2=1.15
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/index_html.diff?cvsroot=cluster&r1=1.20&r2=1.21
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/luci_homebase.css.diff?cvsroot=cluster&r1=1.30&r2=1.31
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/homebase/validate_sys_remove.js.diff?cvsroot=cluster&r1=1.2&r2=1.3
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/cluster_adapters.py.diff?cvsroot=cluster&r1=1.190&r2=1.191
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/homebase_adapters.py.diff?cvsroot=cluster&r1=1.41&r2=1.42
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_bridge.py.diff?cvsroot=cluster&r1=1.51&r2=1.52
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_communicator.py.diff?cvsroot=cluster&r1=1.22&r2=1.23

--- conga/luci/cluster/form-macros	2006/12/20 22:07:16	1.135
+++ conga/luci/cluster/form-macros	2006/12/21 05:08:48	1.136
@@ -208,173 +208,226 @@
 	<tal:block tal:omit-tag=""
 		tal:define="global sessionObj python: request.SESSION.get('checkRet')" />
 
-	<form name="adminform" action="" method="post">
-		<input name="pagetype" id="pagetype" type="hidden" value="6" />
+	<h1>Add a cluster</h1>
 
-		<h1>Add a cluster</h1>
+	<form name="create_cluster" action="" method="post"
+		tal:define="
+			global add_cluster request/SESSION/create_cluster | nothing">
+
+		<input name="pagetype" type="hidden"
+			tal:attributes="value request/form/pagetype | request/pagetype |string:6" />
+
+		<input name="cluster_os" type="hidden"
+			tal:attributes="value add_cluster/cluster_os | nothing" />
 
-		<tal:block tal:condition="python: not sessionObj or not 'requestResults' in sessionObj or not 'nodeList' in sessionObj['requestResults']">
-		<input name="numStorage" type="hidden" value="3" />
 		<table id="systemsTable" class="systemsTable" border="0" 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" id="clusterName" name="clusterName" />
-					</div>
-				</td></tr>
+                <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>
 				<tr class="systemsTable">
-					<th class="systemsTable">System Hostname</th>
-					<th class="systemsTable">Password</th>
+					<th class="systemsTable">Node Hostname</th>
+					<th class="systemsTable">Root Password</th>
+					<tal:block tal:condition="add_cluster">
+						<th class="systemsTable">Key ID</th>
+						<th class="systemsTable">Trust</th>
+					</tal:block>
+					<th></th>
 				</tr>
 			</thead>
 
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<ul class="vanilla deploy">
-						<li class="vanilla"><input type="radio" name="rhn_dl" value="1" checked="checked" />Download packages</li>
-						<li class="vanilla"><input type="radio" name="rhn_dl" value="0" />Use locally installed packages.</li>
+						<li class="vanilla">
+							<input type="radio" name="download_pkgs"
+								value="1" checked="checked" />
+							Download packages
+						</li>
+						<li class="vanilla">
+							<input type="radio" name="download_pkgs"
+								value="0" />
+							Use locally installed packages.
+						</li>
 					</ul>
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<input type="checkbox" value="1" name="enable_storage" />Enable Shared Storage Support
+					<input type="checkbox" name="enable_storage" />
+					Enable Shared Storage Support
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<div>
-						<input type="checkbox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(adminform);"/> Check if cluster node passwords are identical.
-					</div>
+					<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'" />
+							View system certificates before sending any passwords.
+						</li>
+						<li class="vanilla">
+							<input type="checkbox"
+								name="allSameCheckBox" id="allSameCheckBox"
+								onClick="allPasswdsSame(this.form)"
+								tal:attributes="checked python: (add_cluster and add_cluster['identical_passwds']) and 'checked'"
+							/>
+							
+							Check if node passwords are identical.
+						</li>
+					</ul>
 				</td></tr>
-
 				<tr class="systemsTable"><td class="systemsTable" colspan="2">
 					<div class="systemsTableEnd">
-						<input type="button" value="Add Another Row" onClick="addSystem(adminform);" />
+						<input type="button" value="Add another entry"
+							onClick="addSystem(this.form)" />
 					</div>
 				</td></tr>
 			</tfoot>
 
+			<tal:block tal:define="global cur_sysnum python:0" />
+
 			<tbody class="systemsTable">
-				<tr class="systemsTable">
+			 <tal:block
+				tal:condition="exists: add_cluster/nodes"
+				tal:repeat="cur_sys add_cluster/nodes">
+				<tr class="systemsTable"
+					tal:attributes="id python: '__SYSTEM_ROW_%d' % cur_sysnum"
+					tal:define="sys python: add_cluster['nodes'][cur_sys]">
+					<td class="systemsTable">
+						<input type="text"
+							tal:attributes="
+								value sys/host | nothing;
+								id python: '__SYSTEM%d:Addr' % cur_sysnum;
+								name python: '__SYSTEM%d:Addr' % cur_sysnum;
+								class python: 'hbInputSys' + ('errors' in sys and ' error' or '');
+								disabled python: ('auth' in sys and sys['host'].count('.') > 0) and 1 or 0"
+						 />
+					</td>
+					<td class="systemsTable">
+						<tal:block tal:condition="not: exists: sys/auth">
+							<input type="password"
+								onChange="pwd0Change(this.form)"
+								autocomplete="off"
+								tal:attributes="
+									value sys/passwd | nothing;
+									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
+						</tal:block>
+
+						<tal:block tal:condition="exists: sys/auth">
+							<input type="text" onChange="pwd0Change(this.form)"
+								disabled="disabled" value="[authenticated]"
+								tal:attributes="
+									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
+						</tal:block>
+					</td>
+					<td tal:condition="add_cluster" class="systemsTable">
+						<img 
+							tal:attributes="
+								src python: 'trusted' in sys and '/luci/lock-ok.png' or ('fp' in sys and '/luci/lock-closed.png' or '/luci/lock-open.png');
+								title sys/fp | string:no key fingerprint available" />
+						<input type="hidden"
+							tal:attributes="
+								id python: '__SYSTEM%dFingerprint' % cur_sysnum;
+								name python: '__SYSTEM%dFingerprint' % cur_sysnum;
+								value sys/fp | nothing" />
+					</td>
+					<td tal:condition="add_cluster" class="systemsTable">
+						<input type="checkbox" tal:attributes="
+							checked exists: sys/fp;
+							id python: '__SYSTEM%dTrusted' % cur_sysnum;
+							name python: '__SYSTEM%dTrusted' % cur_sysnum;
+							disabled python: 'trusted' in sys"
+						/>
+					</td>
+					<td class="systemsTable">
+						<img src="/luci/delete-row.png" class="deleteRow"
+							title="delete this row"
+							tal:attributes="
+								onclick python: 'delete_element_id(\'__SYSTEM_ROW_%d\')' % cur_sysnum" />
+					</td>
+				</tr>
+				<tal:block
+					tal:define="global cur_sysnum python: cur_sysnum + 1" />
+			 </tal:block>
+
+				<tr class="systemsTable" id="__SYSTEM_ROW_0"
+					tal:condition="not: add_cluster">
 					<td class="systemsTable">
 						<input class="hbInputSys" type="text"
 							id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
 					</td>
 					<td class="systemsTable">
 						<input type="password"
-							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd"
+							onChange="pwd0Change(this.form)"
 							class="hbInputPass" autocomplete="off"
-							onChange="pwd0Change(adminform);" />
+							onChange="pwd0Change(this.form)"
+							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd" />
+					</td>
+					<td class="systemsTable">
+						<img src="/luci/delete-row.png" class="deleteRow"
+							title="delete this row"
+							onclick="delete_element_id('__SYSTEM_ROW_0')" />
 					</td>
 				</tr>
-
-				<tr class="systemsTable">
+				<tr class="systemsTable" id="__SYSTEM_ROW_1"
+					tal:condition="not: add_cluster">
 					<td class="systemsTable">
 						<input class="hbInputSys" type="text"
 							id="__SYSTEM1:Addr" name="__SYSTEM1:Addr" />
 					</td>
 					<td class="systemsTable">
 						<input type="password"
-							id="__SYSTEM1:Passwd" name="__SYSTEM1:Passwd"
+							onChange="pwd0Change(this.form)"
 							class="hbInputPass" autocomplete="off"
-							onChange="pwd0Change(adminform);" />
+							id="__SYSTEM1:Passwd" name="__SYSTEM1:Passwd" />
+					</td>
+					<td class="systemsTable">
+						<img src="/luci/delete-row.png" class="deleteRow"
+							title="delete this row"
+							onclick="delete_element_id('__SYSTEM_ROW_1')" />
 					</td>
 				</tr>
-
-				<tr class="systemsTable">
+				<tr class="systemsTable" id="__SYSTEM_ROW_2"
+					tal:condition="not: add_cluster">
 					<td class="systemsTable">
 						<input class="hbInputSys" type="text"
 							id="__SYSTEM2:Addr" name="__SYSTEM2:Addr" />
 					</td>
 					<td class="systemsTable">
 						<input type="password"
-							id="__SYSTEM2:Passwd" name="__SYSTEM2:Passwd"
+							onChange="pwd0Change(this.form)"
 							class="hbInputPass" autocomplete="off"
-							onChange="pwd0Change(adminform);" />
+							id="__SYSTEM2:Passwd" name="__SYSTEM2:Passwd" />
 					</td>
-				</tr>
-			</tbody>
-		</table>
-		</tal:block>
-
-		<tal:block tal:condition="python: sessionObj and 'requestResults' in sessionObj and 'nodeList' in sessionObj['requestResults']">
-
-		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0">
-			<thead class="systemsTable">
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<div class="systemsTableTop">
-						<strong>Cluster Name:</strong>
-						<input type="text" id="clusterName" name="clusterName"
-							tal:attributes="value python: sessionObj['requestResults']['clusterName']" />
-					</div>
-				</td></tr>
-				<tr class="systemsTable">
-					<th class="systemsTable">Node Hostname</th>
-					<th class="systemsTable">Root Password</th>
-				</tr>
-			</thead>
-
-			<tfoot class="systemsTable">
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<ul class="vanilla deploy">
-						<li class="vanilla"><input type="radio" name="rhn_dl" value="1" checked="checked" />Download packages</li>
-						<li class="vanilla"><input type="radio" name="rhn_dl" value="0" />Use locally installed packages.</li>
-					</ul>
-				</td></tr>
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<input type="checkbox" value="1" name="enable_storage" />Enable Shared Storage Support
-				</td></tr>
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<div>
-						<input type="checkbox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(adminform);"/> Check if cluster node passwords are identical.
-					</div>
-				</td></tr>
-				<tr class="systemsTable"><td class="systemsTable" colspan="2">
-					<div class="systemsTableEnd">
-						<input type="button" value="Add Another Row" onClick="addSystem(adminform);" />
-					</div>
-				</td></tr>
-			</tfoot>
-
-			<span tal:omit-tag="" tal:define="global sysNum python: 0" />
-
-			<tbody class="systemsTable">
-			<tal:block tal:repeat="node python: sessionObj['requestResults']['nodeList']">
-				<span tal:omit-tag=""
-					tal:define="global nodeAuth python: node['cur_auth']" />
-
-				<tr class="systemsTable">
 					<td class="systemsTable">
-						<input type="text"
-							tal:attributes="
-								id python: '__SYSTEM' + str(sysNum) + ':Addr';
-								name python: '__SYSTEM' + str(sysNum) + ':Addr';
-								value python: node['ricci_host'];
-								class python: 'hbInputSys' + ('errors' in node and ' error' or '')"
-						 />
-					</td>
-					<td class="systemsTable">
-						<input
-							onChange="pwd0Change(adminform);"
-							tal:attributes="
-								type python: nodeAuth and 'text' or 'password';
-								value python: nodeAuth and '[authenticated]' or '';
-								class python: 'hbInputPass' + ('errors' in node and ' error' or '');
-								id python: '__SYSTEM' + str(sysNum) + ':Passwd';
-								name python: '__SYSTEM' + str(sysNum) + ':Passwd'"
-						/>
+						<img src="/luci/delete-row.png" class="deleteRow"
+							title="delete this row"
+							onclick="delete_element_id('__SYSTEM_ROW_2')" />
 					</td>
+					<tal:block tal:define="global cur_sysnum python:3" />
 				</tr>
-				<span tal:omit-tag="" tal:define="global sysNum python: sysNum + 1" />
-			</tal:block>
 			</tbody>
 		</table>
-		<input type="hidden" name="numStorage" tal:attributes="value python: sysNum" />
 
-		</tal:block>
+		<input name="numStorage" id="numStorage" type="hidden"
+			tal:attributes="value cur_sysnum" />
 
 		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Submit" value="Submit" onClick="validateForm(document.adminform);" />
+			<input type="button" name="Submit" value="Submit"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
+
+	<div tal:condition="add_cluster">
+		<tal:block
+			tal:define="x python: request.SESSION.delete('create_cluster')" />
+	</div>
 </div>
 
 <div metal:define-macro="clusterconfig-form">
@@ -1018,9 +1071,10 @@
 						<input class="qdscore qdisk" type="text" name="heuristic0:hscore" id="heuristic0:hscore" value="">
 					</td>
 					<td class="systemsTable">
-						<img class="qdscore qdisk qdel_img"
+						<img class="qdisk deleteRow"
 							id="heuristic0:hdel" name="heuristic0:hdel"
-							src="/luci/homebase/x.png"
+							src="/luci/delete-row.png"
+							title="delete this heuristic"
 							onClick="delete_qdisk_heur(this, document.quorum_partition);">
 					</td>
 				</tr>
@@ -1067,8 +1121,9 @@
 								name python: 'heuristic' + str(curHeur) + ':hscore';"/>
 					</td>
 					<td class="systemsTable">
-						<img class="qdscore qdisk qdel_img"
-							src="/luci/homebase/x.png"
+						<img class="qdisk deleteRow"
+							src="/luci/homebase/delete-row.png"
+							title="delete this heuristic"
 							onClick="delete_qdisk_heur(this, document.quorum_partition);"
 							tal:attributes="
 								id python: 'heuristic' + str(curHeur) + ':hdel';
@@ -2804,6 +2859,7 @@
 	<script type="text/javascript">
 		set_page_title('Luci — cluster — Add a new cluster node');
 	</script>
+
 	<script type="text/javascript"
 		src="/luci/homebase/homebase_common.js">
 	</script>
@@ -2812,79 +2868,191 @@
 		src="/luci/homebase/validate_cluster_add.js">
 	</script>
 
-	<input type="hidden" name="clusterName"
-		tal:attributes="value request/form/clusterName | request/clustername | none"
-	/>
 
-	<form name="adminform" action="" method="post">
-		<input name="numStorage" type="hidden" value="1" />
-		<input name="pagetype" type="hidden" value="15" />
-		<input name="addnode" type="hidden" value="1" />
+	<form name="add_node" action="" method="post"
+		tal:define="
+			global add_cluster request/SESSION/add_node | nothing;
+			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="clusterName"
-			tal:attributes="
-				value request/form/clusterName | request/clustername | nothing"
-		/>
+            tal:attributes="value cur_cluster_name | string:[unknown]" />
 
-		<h2>Add a node to <span tal:replace="request/form/clusterName | request/clustername | string:the cluster" /></h2>
+		<input name="pagetype" type="hidden"
+			tal:attributes="value request/form/pagetype | request/pagetype | string:15" />
 
-		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0">
+		<input name="cluster_os" type="hidden"
+			tal:attributes="value add_cluster/cluster_os | nothing" />
+
+		<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> <span tal:content="request/form/clusterName | request/clustername | none" />
-					</div>
-				</td></tr>
 				<tr class="systemsTable">
-					<th class="systemsTable">System Hostname</th>
-					<th class="systemsTable">Password</th>
+					<th class="systemsTable">Node Hostname</th>
+					<th class="systemsTable">Root Password</th>
+					<tal:block tal:condition="add_cluster">
+						<th class="systemsTable">Key ID</th>
+						<th class="systemsTable">Trust</th>
+					</tal:block>
+					<th></th>
 				</tr>
 			</thead>
 
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
 					<ul class="vanilla deploy">
-						<li class="vanilla"><input type="radio" name="rhn_dl" value="1" checked="checked" />Download packages</li>
-						<li class="vanilla"><input type="radio" name="rhn_dl" value="0" />Use locally installed packages.</li>
+						<li class="vanilla">
+							<input type="radio" name="download_pkgs" value="1"
+								tal:attributes="
+									checked add_system/download_pkgs | string:checked" />
+							Download packages
+						</li>
+						<li class="vanilla">
+							<input type="radio" name="download_pkgs" value="0"
+								tal:attributes="
+									checked not: add_system/download_pkgs | nothing" />
+							
+							Use locally installed packages.
+						</li>
 					</ul>
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<input type="checkbox" value="1" name="enable_storage" />Enable Shared Storage Support
+					<input type="checkbox" name="enable_storage"
+						tal:attributes="
+							checked add_system/shared_storage | nothing" />
+					Enable Shared Storage Support
 				</td></tr>
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<div id="allSameDiv">
-						<input type="checkbox" class="allSameCheckBox"
-							name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(adminform);"/>
-						Check if cluster node passwords are identical.
-					</div>
+					<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'" />
+							View system certificates before sending any passwords.
+						</li>
+						<li class="vanilla"
+							tal:attributes="id python: (not add_cluster or ('nodes' in add_cluster and len(add_cluster['nodes']) < 2)) and 'allSameDiv'">
+							<input type="checkbox"
+								name="allSameCheckBox" id="allSameCheckBox"
+								onClick="allPasswdsSame(this.form)"
+								tal:attributes="checked python: (add_cluster and add_cluster['identical_passwds']) and 'checked'"
+							/>
+							Check if node passwords are identical.
+						</li>
+					</ul>
 				</td></tr>
-
 				<tr class="systemsTable"><td class="systemsTable" colspan="2">
 					<div class="systemsTableEnd">
-						<input type="button" value="Add Another Row" onClick="addSystem(adminform);" />
+						<input type="button" value="Add another entry"
+							onClick="addSystem(this.form)" />
 					</div>
 				</td></tr>
 			</tfoot>
 
+			<tal:block tal:define="global cur_sysnum python:0" />
+
 			<tbody class="systemsTable">
-				<tr class="systemsTable">
+			 <tal:block
+				tal:condition="exists: add_cluster/nodes"
+				tal:repeat="cur_sys add_cluster/nodes">
+				<tr class="systemsTable"
+					tal:attributes="id python: '__SYSTEM_ROW_%d' % cur_sysnum"
+					tal:define="sys python: add_cluster['nodes'][cur_sys]">
+					<td class="systemsTable">
+						<input type="text"
+							tal:attributes="
+								value sys/host | nothing;
+								id python: '__SYSTEM%d:Addr' % cur_sysnum;
+								name python: '__SYSTEM%d:Addr' % cur_sysnum;
+								class python: 'hbInputSys' + ('errors' in sys and ' error' or '');
+								disabled python: ('auth' in sys and sys['host'].count('.') > 0) and 1 or 0"
+						 />
+					</td>
+					<td class="systemsTable">
+						<tal:block tal:condition="not: exists: sys/auth">
+							<input type="password"
+								autocomplete="off"
+								onChange="pwd0Change(this.form)"
+								tal:attributes="
+									value sys/passwd | nothing;
+									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
+						</tal:block>
+
+						<tal:block tal:condition="exists: sys/auth">
+							<input type="text" onChange="pwd0Change(this.form)"
+								disabled="disabled" value="[authenticated]"
+								tal:attributes="
+									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
+						</tal:block>
+					</td>
+					<td tal:condition="add_cluster" class="systemsTable">
+						<img 
+							tal:attributes="
+								src python: 'trusted' in sys and '/luci/lock-ok.png' or ('fp' in sys and '/luci/lock-closed.png' or '/luci/lock-open.png');
+								title sys/fp | string:no key fingerprint available" />
+						<input type="hidden"
+							tal:attributes="
+								id python: '__SYSTEM%dFingerprint' % cur_sysnum;
+								name python: '__SYSTEM%dFingerprint' % cur_sysnum;
+								value sys/fp | nothing" />
+					</td>
+					<td tal:condition="add_cluster" class="systemsTable">
+						<input type="checkbox" tal:attributes="
+							checked exists: sys/fp;
+							id python: '__SYSTEM%dTrusted' % cur_sysnum;
+							name python: '__SYSTEM%dTrusted' % cur_sysnum;
+							disabled python: 'trusted' in sys"
+						/>
+					</td>
+					<td class="systemsTable">
+						<img src="/luci/delete-row.png" class="deleteRow"
+							title="delete this row"
+							tal:attributes="
+								onclick python: 'delete_element_id(\'__SYSTEM_ROW_%d\')' % cur_sysnum" />
+					</td>
+				</tr>
+				<tal:block
+					tal:define="global cur_sysnum python: cur_sysnum + 1" />
+			 </tal:block>
+
+				<tr class="systemsTable" id="__SYSTEM_ROW_0"
+					tal:condition="not: add_cluster">
 					<td class="systemsTable">
 						<input class="hbInputSys" type="text"
 							id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
 					</td>
 					<td class="systemsTable">
 						<input type="password"
-							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd"
+							onChange="pwd0Change(this.form)"
 							class="hbInputPass" autocomplete="off"
-							onChange="pwd0Change(adminform);" />
+							onChange="pwd0Change(this.form)"
+							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd" />
+					</td>
+					<td class="systemsTable">
+						<img src="/luci/delete-row.png" class="deleteRow"
+							title="delete this row"
+							onclick="delete_element_id('__SYSTEM_ROW_0')" />
 					</td>
+					<tal:block tal:define="global cur_sysnum python:1" />
 				</tr>
 			</tbody>
 		</table>
 
+		<input name="numStorage" id="numStorage" type="hidden"
+			tal:attributes="value cur_sysnum" />
+
 		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Submit" value="Submit" onClick="validateForm(document.adminform);" />
+			<input type="button" name="Submit" value="Submit"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
+
+	<div tal:condition="add_cluster">
+		<tal:block
+			tal:define="x python: request.SESSION.delete('add_node')" />
+	</div>
 </div>
 
 <div metal:define-macro="nodeprocess-form">
@@ -2893,14 +3061,14 @@
 
 		<div id="errmsgsdiv" class="errmsgs"
 			tal:condition="python: result and len(result) > 1 and 'errors' in result[1]">
-            <p class="errmsgs">The following errors occurred:</p>
+			<p class="errmsgs">The following errors occurred:</p>
 
-            <ul class="errmsgs">
-                <tal:block tal:repeat="e python: result[1]['errors']">
-                    <li class="errmsgs" tal:content="python:e" />
-                </tal:block>
-            </ul>
-        </div>
+			<ul class="statusmsg">
+				<tal:block tal:repeat="e python: result[1]['errors']">
+					<li class="statusmsg" tal:content="python:e" />
+				</tal:block>
+			</ul>
+		</div>
 	</tal:block>
 </div>
 
--- conga/luci/cluster/index_html	2006/11/29 18:39:50	1.29
+++ conga/luci/cluster/index_html	2006/12/21 05:08:48	1.30
@@ -207,23 +207,24 @@
 		<tal:block tal:define="ret python: request.SESSION.get('checkRet')">
 		<div class="retmsgs" id="retmsgsdiv" tal:condition="python:(ret and 'messages' in ret and len(ret['messages']))">
 			<div class="hbclosebox">
-				<a href="javascript:hide_element('retmsgsdiv');"><img src="../homebase/x.png"></a>
+				<a href="javascript:hide_element('retmsgsdiv')"><img src="/luci/homebase/x.png" class="closeBox" title="dismiss"></a>
 			</div>
-			<ul class="retmsgs">
+			<p class="retmsgs">Status messages:</p>
+			<ul class="statusmsg">
 				<tal:block tal:repeat="e python:ret['messages']">
-					<li class="retmsgs" tal:content="python:e" />
+					<li class="statusmsg" tal:content="python:e" />
 				</tal:block>
 			</ul>
 		</div>
 
 		<div id="errmsgsdiv" class="errmsgs" tal:condition="python:(ret and 'errors' in ret and len(ret['errors']))">
 			<div class="hbclosebox">
-				<a class="hbclosebox" href="javascript:hide_element('errmsgsdiv');"><img src="../homebase/x.png"></a>
+				<a class="hbclosebox" href="javascript:hide_element('errmsgsdiv')"><img src="/luci/homebase/x.png" class="closeBox" title="dismiss"></a>
 			</div>
 			<p class="errmsgs">The following errors occurred:</p>
-			<ul class="errmsgs">
+			<ul class="statusmsg">
 				<tal:block tal:repeat="e python:ret['errors']">
-					<li class="errmsgs" tal:content="python:e" />
+					<li class="statusmsg" tal:content="python:e" />
 				</tal:block>
 			</ul>
 		</div>
--- conga/luci/cluster/validate_config_qdisk.js	2006/10/04 17:24:58	1.4
+++ conga/luci/cluster/validate_config_qdisk.js	2006/12/21 05:08:48	1.5
@@ -314,10 +314,11 @@
 	var del_td = document.createElement('td');
 	del_td.className = 'systemsTable';
 	var del_img = document.createElement('img');
-	del_img.className = 'qdscore qdisk qdel_img';
+	del_img.className = 'qdisk deleteRow';
 	del_img.setAttribute('name', hstr + ':hdel');
 	del_img.setAttribute('id', hstr + ':hdel');
-	del_img.setAttribute('src', '/luci/homebase/x.png');
+	del_img.setAttribute('src', '/luci/delete-row.png');
+	del_img.setAttribute('title', 'delete this row');
 	del_img.setAttribute('onClick', 'delete_qdisk_heur(this, document.quorum_partition)');
 	del_td.appendChild(del_img);
 
--- conga/luci/homebase/form-macros	2006/11/01 23:04:17	1.49
+++ conga/luci/homebase/form-macros	2006/12/21 05:08:48	1.50
@@ -71,7 +71,8 @@
 			tal:attributes="value python:data['children'][data['curIndex']]['absolute_url']" />
 
 		<div class="hbSubmit" tal:condition="python:userList" id="hbSubmit">
-			<input name="Submit" type="button" value="Delete This User" onClick="validateForm(document.adminform);" />
+			<input name="Submit" type="button" value="Delete This User"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 
@@ -140,7 +141,8 @@
 			tal:attributes="value python:data['children'][data['curIndex']]['absolute_url']" />
 
 		<div class="hbSubmit" id="hbSubmit">
-			<input name="Submit" type="button" value="Submit" onClick="validateForm(document.adminform);" />
+			<input name="Submit" type="button" value="Submit"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 </div>
@@ -196,7 +198,7 @@
 
 		<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 = adminform.baseURL.value + '&user=' + adminform.userList.options[adminform.userList.selectedIndex].text">
+		<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">
 				<option class="homebase"
 					tal:content="python:user"
@@ -250,12 +252,14 @@
 			tal:attributes="value python: num_clusters + 1" />
 
 		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Update Permissions" value="Update Permissions" onClick="validateForm(document.adminform);" />
+			<input type="button" name="Update Permissions" value="Update Permissions"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 
 	<div tal:condition="python: blankForm">
-		<p>Either no users have been added or no clusters or storage systems are being managed by Luci.</p>
+		<p>Either no users have been added or no clusters
+			or storage systems are managed by Luci.</p>
 	</div>
 </div>
 
@@ -287,93 +291,191 @@
 		set_page_title('Luci — homebase — Remove a system or cluster from Luci');
 	</script>
 
-	<span tal:omit-tag=""
-		tal:define="global systems python:here.getSystems();
-					global blankForm python:1;
-					global num_clusters python:-1;
-					global num_systems python:-1"
-	/>
-
 	<h2 class="homebase">Manage Systems and Clusters</h2>
 
 	<h3>Authenticate to Storage or Cluster Systems</h3>
 
 	<form name="authform" method="post" action="">
-		<input type="hidden" name="pagetype" value="8" />
-		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0">
-			<thead class="systemsTable">
-				<tr class="systemsTable">
-					<th class="systemsTable">System Hostname</th>
-					<th class="systemsTable">Root Password</th>
-				</tr>
-			</thead>
+		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0"
+			tal:define="
+				new_systems request/SESSION/auth_systems | nothing;
+				global cur_sysnum python: 1">
 
-			<tfoot class="systemsTable">
-				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<div id="allSameDiv" class="invisible">
-						<input type="checkbox" class="allSameCheckBox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(this.form);"/><span>Check if storage system passwords are identical.</span>
-					</div>
-				</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:condition="not: new_systems">
+				<thead class="systemsTable">
+					<tr class="systemsTable">
+						<th class="systemsTable">System Hostname</th>
+						<th class="systemsTable">Root Password</th>
+						<th class="systemsTable"></th>
+					</tr>
+				</thead>
+			
+				<tbody class="systemsTable">
+					<tr class="systemsTable" id="__SYSTEM_ROW_0">
+						<td class="systemsTable">
+							<input class="hbInputSys" type="text"
+								id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
+						</td>
+						<td class="systemsTable">
+							<input type="password"
+								autocomplete="off"
+								id="__SYSTEM0:Passwd"
+								name="__SYSTEM0:Passwd"
+								class="hbInputPass"
+								onChange="pwd0Change(this.form)" />
+						</td>
+						<td class="systemsTable">
+							<img src="/luci/delete-row.png" class="deleteRow"
+								title="delete this row"
+								onclick="delete_element_id('__SYSTEM_ROW_0')" />
+						</td>
+					</tr>
+				</tbody>
+			</tal:block>
 
-			<tbody class="systemsTable">
-				<tr class="systemsTable">
-					<td class="systemsTable">
-						<input class="hbInputSys" type="text"
-							id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
-					</td>
-					<td class="systemsTable">
-						<input type="password" autocomplete="off"
-							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd"
-							class="hbInputPass"
-							onChange="pwd0Change(this.form);" />
-					</td>
-				</tr>
+			<tal:block tal:condition="new_systems">
+				<thead class="systemsTable">
+					<tr class="systemsTable">
+						<th class="systemsTable">System Hostname</th>
+						<th class="systemsTable">Root Password</th>
+						<th class="systemsTable">Key ID</th>
+						<th class="systemsTable">Trust</th>
+						<th class="systemsTable"></th>
+					</tr>
+				</thead>
+				<tal:block tal:define="global cur_sysnum python: 0" />
 
-				<tal:block tal:define="global numsys python: 0" />
+				<tbody class="systemsTable">
+				<tal:block tal:repeat="cur_sys new_systems">
+					<tr class="systemsTable"
+						tal:attributes="
+							id python: '__SYSTEM_ROW_%d' % cur_sysnum"
+						tal:define="sys python: new_systems[cur_sys]">
 
-				<tal:block tal:repeat="s python:systems[2]">
-					<tal:block tal:define="global numsys python: numsys + 1" />
-					<tr class="systemsTable">
 						<td class="systemsTable">
 							<input class="hbInputSys" type="text"
 								tal:attributes="
-									id python: '__SYSTEM' + str(numsys) + ':Addr';
-									name python: '__SYSTEM' + str(numsys) + ':Addr'" />
+									id python: '__SYSTEM%d:Addr' % cur_sysnum;
+									name python: '__SYSTEM%d:Addr' % cur_sysnum;
+									value sys/host | nothing" />
 						</td>
-
 						<td class="systemsTable">
-							<input type="password" autocomplete="off"
-								onChange="pwd0Change(this.form);"
+							<input type="password"
+								autocomplete="off"
 								class="hbInputPass"
+								onChange="pwd0Change(this.form)"
+								tal:attributes="
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									value sys/passwd | nothing" />
+						</td>
+						<td class="systemsTable">
+							<img 
 								tal:attributes="
-									id python: '__SYSTEM' + str(numsys) + ':Password';
-									name python: '__SYSTEM' + str(numsys) + ':Password'" />
+									src python: 'trusted' in sys and '/luci/lock-ok.png' or ('fp' in sys and '/luci/lock-closed.png' or '/luci/lock-open.png');
+									title sys/fp | string:no key fingerprint available"
+							/>
+							<input type="hidden"
+								tal:attributes="
+									id python: '__SYSTEM%dFingerprint' % cur_sysnum;
+									name python: '__SYSTEM%dFingerprint' % cur_sysnum;
+									value sys/fp | nothing" />
+						</td>
+						<td class="systemsTable">
+							<input type="checkbox" checked tal:attributes="
+								id python: '__SYSTEM%dTrusted' % cur_sysnum;
+								name python: '__SYSTEM%dTrusted' % cur_sysnum;
+								disabled python: 'trusted' in sys"
+							/>
+						</td>
+						<td class="systemsTable">
+							<img src="/luci/delete-row.png" class="deleteRow"
+								title="delete this row"
+								tal:attributes="onclick python:'delete_element_id(\'__SYSTEM_ROW_%d\')' % cur_sysnum" />
 						</td>
 					</tr>
+					<tal:block
+						tal:define="global cur_sysnum python: cur_sysnum + 1" />
 				</tal:block>
 			</tbody>
+			<tal:block
+				tal:define="
+					x python: request.SESSION.delete('auth_systems')" />
+			</tal:block>
+
+			<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"
+							tal:attributes="id python: cur_sysnum < 2 and 'allSameDiv' or ''">
+							<input type="checkbox" name="allSameCheckBox"
+								id="allSameCheckBox" onClick="allPasswdsSame(this.form)" />
+							Check if system passwords are identical.
+						</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>
 		</table>
 
-		<input type="hidden" name="numStorage" value="1" />
+		<input name="numStorage" id="numStorage" type="hidden"
+			tal:attributes="value cur_sysnum | string:1" />
+
+		<input type="hidden" name="pagetype" value="8" />
 
 		<div class="hbSubmit" id="hbSubmit">
 			<input type="button" name="Submit" value="Submit"
 				onClick="validateAuth(this.form)" />
 		</div>
+
+		<tal:block tal:condition="exists: request/SESSION/auth_status">
+			<div class="retmsgs" id="auth_retmsgsdiv"
+				tal:condition="exists: request/SESSION/auth_status/messages">
+				<div class="hbclosebox">
+					<a href="javascript:hide_element('auth_retmsgsdiv');"><img src="x.png" class="closeBox" title="dismiss"></a>
+				</div>
+				<p class="retmsgs">Status messages:</p>
+				<ul class="statusmsg">
+					<tal:block tal:repeat="e request/SESSION/auth_status/messages">
+						<li class="statusmsg" tal:content="e" />
+					</tal:block>
+				</ul>
+			</div>
+			<div class="errmsgs" id="auth_errmsgsdiv"
+				tal:condition="exists: request/SESSION/auth_status/errors">
+				<div class="hbclosebox">
+					<a href="javascript:hide_element('auth_errmsgsdiv');"><img src="x.png" class="closeBox" title="dismiss"></a>
+				</div>
+				<p class="errmsgs">The following errors occurred:</p>
+				<ul class="statusmsg">
+					<tal:block tal:repeat="e request/SESSION/auth_status/errors">
+						<li class="statusmsg" tal:content="e" />
+					</tal:block>
+				</ul>
+			</div>
+			<tal:block
+				tal:define="x python: request.SESSION.delete('auth_status')" />
+			<div class="padding"> </div>
+		</tal:block>
 	</form>
 
+	<tal:block tal:define="
+		global systems python:here.getSystems();
+		global blankForm python:1;
+		global num_clusters python:-1;
+		global num_systems python:-1" />
+
 	<form name="adminform" method="post" action=""
 		tal:condition="python:(systems[0] and len(systems[0]) > 0) or (systems[1] and len(systems[1]) > 0)">
 
-		<span tal:omit-tag="" tal:define="global blankForm python:0" />
-
+		<tal:block tal:define="global blankForm python:0" />
 
 		<input type="hidden" name="pagetype"
 			tal:attributes="value request/form/pagetype | request/pagetype | nothing" />
@@ -425,19 +527,20 @@
 			</tal:block>
 		</div>
 
-		<input type="hidden" id="numStorage"
+		<input type="hidden" id="num_storage"
 			tal:attributes="value python: num_systems + 1" />
 
-		<input type="hidden" id="numClusters"
+		<input type="hidden" id="num_clusters"
 			tal:attributes="value python: num_clusters + 1" />
 
-		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Submit" value="Remove Selected Systems" onClick="validateForm(document.adminform);" />
+		<div class="hbSubmit">
+			<input type="button" name="Submit" value="Remove selected entries"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 
 	<div tal:condition="python: blankForm">
-		<p>No clusters or storage systems are currently being managed by Luci.</p>
+		<p>No clusters or storage systems are currently managed by Luci.</p>
 	</div>
 </div>
 
@@ -476,51 +579,141 @@
 		<input name="pagetype" type="hidden"
 			tal:attributes="value request/form/pagetype | request/pagetype | nothing" />
 
-		<input name="numStorage" id="numStorage" type="hidden" value="1" />
-
 		<input name="absoluteURL" type="hidden"
 			tal:attributes="value python:data['children'][data['curIndex']]['absolute_url']" />
 
-		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0">
-			<thead class="systemsTable">
-				<tr class="systemsTable">
-					<th class="systemsTable">System Hostname</th>
-					<th class="systemsTable">Root Password</th>
-				</tr>
-			</thead>
+		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0"
+			tal:define="
+				new_systems request/SESSION/add_systems | nothing;
+				global cur_sysnum python: 1">
+
+			<tal:block tal:condition="not: new_systems">
+				<thead class="systemsTable">
+					<tr class="systemsTable">
+						<th class="systemsTable">System Hostname</th>
+						<th class="systemsTable">Root Password</th>
+						<th class="systemsTable"></th>
+					</tr>
+				</thead>
+			
+				<tbody class="systemsTable">
+					<tr class="systemsTable" id="__SYSTEM_ROW_0">
+						<td class="systemsTable">
+							<input class="hbInputSys" type="text"
+								id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
+						</td>
+						<td class="systemsTable">
+							<input type="password"
+								autocomplete="off"
+								id="__SYSTEM0:Passwd"
+								name="__SYSTEM0:Passwd"
+								class="hbInputPass"
+								onChange="pwd0Change(this.form)" />
+						</td>
+						<td class="systemsTable">
+							<img src="/luci/delete-row.png" class="deleteRow"
+								title="delete this row"
+								onclick="delete_element_id('__SYSTEM_ROW_0')" />
+						</td>
+					</tr>
+				</tbody>
+			</tal:block>
+
+			<tal:block tal:condition="new_systems">
+				<thead class="systemsTable">
+					<tr class="systemsTable">
+						<th class="systemsTable">System Hostname</th>
+						<th class="systemsTable">Root Password</th>
+						<th class="systemsTable">Key ID</th>
+						<th class="systemsTable">Trust</th>
+						<th class="systemsTable"></th>
+					</tr>
+				</thead>
+				<tal:block tal:define="global cur_sysnum python: 0" />
+
+				<tbody class="systemsTable">
+				<tal:block tal:repeat="cur_sys new_systems">
+					<tr class="systemsTable"
+						tal:attributes="
+							id python: '__SYSTEM_ROW_%d' % cur_sysnum"
+						tal:define="sys python: new_systems[cur_sys]">
+
+						<td class="systemsTable">
+							<input class="hbInputSys" type="text"
+								tal:attributes="
+									id python: '__SYSTEM%d:Addr' % cur_sysnum;
+									name python: '__SYSTEM%d:Addr' % cur_sysnum;
+									value sys/host | nothing" />
+						</td>
+						<td class="systemsTable">
+							<input type="password"
+								autocomplete="off"
+								class="hbInputPass"
+								onChange="pwd0Change(this.form)"
+								tal:attributes="
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									value sys/passwd | nothing" />
+									
+						</td>
+						<td class="systemsTable">
+							<img 
+								tal:attributes="
+									src python: 'trusted' in sys and '/luci/lock-ok.png' or ('fp' in sys and '/luci/lock-closed.png' or '/luci/lock-open.png');
+									title sys/fp | string:no key fingerprint available"
+							/>
+							<input type="hidden"
+								tal:attributes="
+									id python: '__SYSTEM%dFingerprint' % cur_sysnum;
+									name python: '__SYSTEM%dFingerprint' % cur_sysnum;
+									value sys/fp | nothing" />
+						</td>
+						<td class="systemsTable">
+							<input type="checkbox" checked tal:attributes="
+								id python: '__SYSTEM%dTrusted' % cur_sysnum;
+								name python: '__SYSTEM%dTrusted' % cur_sysnum;
+								disabled python: 'trusted' in sys"
+							/>
+						</td>
+						<td class="systemsTable">
+							<img src="/luci/delete-row.png" class="deleteRow"
+								title="delete this row"
+								tal:attributes="onclick python:'delete_element_id(\'__SYSTEM_ROW_%d\')' % cur_sysnum" />
+						</td>
+					</tr>
+					<tal:block
+						tal:define="global cur_sysnum python: cur_sysnum + 1" />
+				</tal:block>
+			</tbody>
+			<tal:block
+				tal:define="
+					x python: request.SESSION.delete('add_systems')" />
+			</tal:block>
 
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<div id="allSameDiv">
-						<input type="checkbox" class="allSameCheckBox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(adminform);"/><span>Check if storage system passwords are identical.</span>
-					</div>
+					<ul class="vanilla">
+						<li class="vanilla"><input name="check_certs" 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>
 				</td></tr>
 
 				<tr class="systemsTable"><td class="systemsTable" colspan="2">
 					<div class="systemsTableEnd">
-						<input type="button" value="Add another entry" onClick="addSystem(adminform);" />
+						<input type="button" value="Add another entry"
+							onClick="addSystem(this.form)" />
 					</div>
 				</td></tr>
 			</tfoot>
-
-			<tbody class="systemsTable">
-				<tr class="systemsTable">
-					<td class="systemsTable">
-						<input class="hbInputSys" type="text"
-							id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
-					</td>
-					<td class="systemsTable">
-						<input type="password" autocomplete="off"
-							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd"
-							class="hbInputPass"
-							onChange="pwd0Change(adminform);" />
-					</td>
-				</tr>
-			</tbody>
 		</table>
 
+		<input name="numStorage" id="numStorage" type="hidden"
+			tal:attributes="value cur_sysnum | string:1" />
+
 		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Submit" value="Submit" onClick="validateForm(document.adminform);" />
+			<input type="button" name="Submit" value="Submit"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 </div>
@@ -550,95 +743,147 @@
 		set_page_title('Luci — homebase — Add a running cluster to be managed by Luci');
 	</script>
 
-	<tal:block tal:define="
-		global sessionObj python:request.SESSION.get('checkRet')" />
-
 	<h2 class="homebase">Add Cluster</h2>
 
+	<tal:block tal:define="
+		global add_cluster request/SESSION/add_cluster | nothing" />
+
 	<form name="adminform" action="" method="post"
-		tal:condition="python: sessionObj and len(sessionObj)">
+		tal:condition="add_cluster">
+
 		<input name="pagetype" type="hidden"
 			tal:attributes="value request/form/pagetype | request/pagetype | nothing" />
 
 		<input name="absoluteURL" type="hidden"
 			tal:attributes="value python:data['children'][data['curIndex']]['absolute_url']" />
 
+		<input name="pass" type="hidden"
+			tal:attributes="value add_cluster/pass | string:0" />
+
+		<input name="cluster_os" type="hidden"
+			tal:attributes="value add_cluster/cluster_os | string:rhel5" />
+
 		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0">
 			<thead class="systemsTable">
 				<tr class="systemsTable"><td class="systemsTable" colspan="2">
 					<div class="systemsTableTop">
-						<strong>Cluster Name:</strong> <span tal:replace="python: sessionObj['requestResults']['clusterName']" />
-						<input type="hidden" type="text" id="clusterName" name="clusterName" tal:attributes="value python: sessionObj['requestResults']['clusterName']" />
+						<strong class="cluster_name">Cluster Name:
+							<span tal:replace="add_cluster/name | string:[unknown]" />
+						</strong>
+						<input type="hidden" id="clusterName" name="clusterName"
+							tal:attributes="value add_cluster/name | nothing" />
 					</div>
 				</td></tr>
+
 				<tr class="systemsTable">
 					<th class="systemsTable">Node Hostname</th>
 					<th class="systemsTable">Root Password</th>
+					<th class="systemsTable">Key ID</th>
+					<th class="systemsTable">Trust</th>
 				</tr>
+
 			</thead>
 
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<div tal:condition="python: not 'isComplete' in sessionObj['requestResults'] or not sessionObj['requestResults']['isComplete'] or ('errors' in sessionObj and len(sessionObj['errors']) > 0)">
-						<input type="checkbox" name="allSameCheckBox" id="allSameCheckBox" onClick="allPasswdsSame(adminform);"/> Check if cluster node passwords are identical.
-					</div>
-					<div class="systemsTable"
-						tal:condition="python: 'isComplete' in sessionObj['requestResults'] and sessionObj['requestResults']['isComplete']"> </div>
+					<ul class="vanilla">
+						<li class="vanilla">
+							<input name="check_certs" type="checkbox"
+								tal:attributes="checked python: add_cluster['check_certs'] and 'checked'" />
+							View system certificates before sending any passwords.
+						</li>
+						<li class="vanilla" id="allSameDiv">
+						<li class="vanilla" tal:condition="not: exists: add_cluster/complete">
+							<input type="checkbox" name="allSameCheckBox"
+								id="allSameCheckBox" onClick="allPasswdsSame(this.form)"
+								tal:attributes="checked python: add_cluster['identical_passwds'] and 'checked'"
+							/>
+							
+							Check if node passwords are identical.
+						</li>
+						<li class="vanilla" tal:condition="python: add_cluster['pass'] > 0 and 'incomplete' in add_cluster">
+							<input type="checkbox" name="asis">
+							Add the cluster to Luci as-is.<br>
+							Any nodes that are not authenticated will need to be authenticated later.
+						</li>
+					</ul>
+					<br/>
 				</td></tr>
 			</tfoot>
 
-			<span tal:omit-tag=""
-				tal:define="global sysNum python: 0"
-			/>
-
-			<tbody class="systemsTable" tal:condition="python: 'nodeList' in sessionObj['requestResults']">
-			<tal:block tal:repeat="node python: sessionObj['requestResults']['nodeList']">
-				<span tal:omit-tag=""
-					tal:define="global nodeAuth python: node['cur_auth']" />
+			<tal:block tal:define="global cur_sysnum python:0" />
 
-				<tr class="systemsTable">
+			<tbody class="systemsTable" tal:condition="add_cluster/nodes">
+			 <tal:block tal:repeat="cur_sys add_cluster/nodes">
+				<tr class="systemsTable"
+					tal:define="sys python: add_cluster['nodes'][cur_sys]">
 					<td class="systemsTable">
 						<input type="text"
 							tal:attributes="
-								id python: '__SYSTEM' + str(sysNum) + ':Addr';
-								name python: '__SYSTEM' + str(sysNum) + ':Addr';
-								value python: node['host'];
-								class python: 'hbInputSys' + ('errors' in node and ' error' or '');
-								disabled python: (nodeAuth and node['host'].count('.') > 0) and 1 or 0"
+								value sys/host | nothing;
+								id python: '__SYSTEM%d:Addr' % cur_sysnum;
+								name python: '__SYSTEM%d:Addr' % cur_sysnum;
+								class python: 'hbInputSys' + ('errors' in sys and ' error' or '');
+								disabled python: ('auth' in sys and sys['host'].count('.') > 0) and 1 or 0"
 						 />
 					</td>
 					<td class="systemsTable">
-						<input onChange="pwd0Change(adminform);"
+						<tal:block tal:condition="not: exists: sys/auth">
+							<input type="password"
+								autocomplete="off"
+								onChange="pwd0Change(this.form)"
+								tal:attributes="
+									value sys/passwd | nothing;
+									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
+						</tal:block>
+
+						<tal:block tal:condition="exists: sys/auth">
+							<input type="text" onChange="pwd0Change(this.form)"
+								disabled="disabled" value="[authenticated]"
+								tal:attributes="
+									class python: 'hbInputPass' + ('errors' in sys and ' error' or '');
+									id python: '__SYSTEM%d:Passwd' % cur_sysnum;
+									name python: '__SYSTEM%d:Passwd' % cur_sysnum" />
+						</tal:block>
+					</td>
+					<td class="systemsTable">
+						<img 
 							tal:attributes="
-								type python: nodeAuth and 'text' or 'password';
-								value python: nodeAuth and '[authenticated]' or '';
-								class python: 'hbInputPass' + ('errors' in node and ' error' or '');
-								id python: '__SYSTEM' + str(sysNum) + ':Passwd';
-								name python: '__SYSTEM' + str(sysNum) + ':Passwd';
-								disabled python: nodeAuth and 1 or 0"
+								src python: 'trusted' in sys and '/luci/lock-ok.png' or ('fp' in sys and '/luci/lock-closed.png' or '/luci/lock-open.png');
+								title sys/fp | string:no key fingerprint available" />
+						<input type="hidden"
+							tal:attributes="
+								id python: '__SYSTEM%dFingerprint' % cur_sysnum;
+								name python: '__SYSTEM%dFingerprint' % cur_sysnum;
+								value sys/fp | nothing" />
+					</td>
+					<td class="systemsTable">
+						<input type="checkbox" tal:attributes="
+							checked python: add_cluster['pass'] > 0;
+							id python: '__SYSTEM%dTrusted' % cur_sysnum;
+							name python: '__SYSTEM%dTrusted' % cur_sysnum;
+							disabled python: 'trusted' in sys"
 						/>
 					</td>
 				</tr>
-				<span tal:omit-tag=""
-					tal:define="global sysNum python: sysNum + 1"
-				/>
-			</tal:block>
+				<tal:block tal:define="global cur_sysnum python: cur_sysnum + 1" />
+			 </tal:block>
 			</tbody>
 		</table>
 
 		<input name="numStorage" id="numStorage" type="hidden"
-			tal:attributes="value python: sysNum" />
+			tal:attributes="value cur_sysnum" />
 
 		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Submit" value="Add This Cluster" onClick="validateForm(document.adminform);" />
+			<input type="button" name="Submit" value="Add This Cluster"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 
-	<div tal:condition="python: not sessionObj or not len(sessionObj)">
-		<span class="error">
-			A data integrity error has occurred. Please attempt to add this cluster to the Luci management interface again.
-		</span>
-		<tal:block tal:define="nop python:here.abortManageCluster(request)" />
+	<div tal:condition="add_cluster">
+		<tal:block tal:define="x python: request.SESSION.delete('add_cluster')" />
 	</div>
 </div>
 
@@ -675,19 +920,26 @@
 
 		<p class="hbText">Enter one node from the cluster you wish to add to the Luci management interface.</p>
 
-		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0">
+		<table id="systemsTable" class="systemsTable" border="0" cellspacing="0"
+			tal:define="cur_sys request/SESSION/add_cluster_initial | nothing">
+
 			<thead class="systemsTable">
 				<tr class="systemsTable">
 					<th class="systemsTable">System Hostname</th>
 					<th class="systemsTable">Root Password</th>
+					<tal:block tal:condition="cur_sys">
+						<th>Key Id</th>
+						<th>Trust</th>
+					</tal:block>
 				</tr>
 			</thead>
 
 			<tfoot class="systemsTable">
 				<tr class="systemsTable"><td colspan="2" class="systemsTable">
-					<div class="hbcheckdiv">
-						<input type="checkbox" name="allSameCheckBox" id="allSameCheckBox" /> Attempt to authenticate to all cluster nodes using the password provided above.
-					</div>
+					<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>
+					</ul>
 				</td></tr>
 			</tfoot>
 
@@ -695,21 +947,42 @@
 				<tr class="systemsTable">
 					<td class="systemsTable">
 						<input class="hbInputSys" type="text"
-							id="__SYSTEM0:Addr" name="__SYSTEM0:Addr" />
+							id="__SYSTEM0:Addr" name="__SYSTEM0:Addr"
+							tal:attributes="
+								value cur_sys/host | nothing" />
 					</td>
 					<td class="systemsTable">
-						<input type="password" autocomplete="off"
+						<input class="hbInputPass" type="password"
+							onChange="pwd0Change(this.form)"
+							autocomplete="off"
 							id="__SYSTEM0:Passwd" name="__SYSTEM0:Passwd"
-							class="hbInputPass" />
+							tal:attributes="
+								value cur_sys/passwd | nothing" />
 					</td>
+					<tal:block tal:condition="cur_sys">
+						<td class="systemsTable">
+							<img tal:attributes="
+								title sys/fp | string:no key fingerprint available;
+								src python: 'trusted' in cur_sys and '/luci/lock-ok.png' or ('fp' in cur_sys and '/luci/lock-closed.png' or '/luci/lock-open.png')"
+							/>
+						</td>
+						<td class="systemsTable">
+							<input type="checkbox" name="host_is_trusted" checked="checked" />
+						</td>
+					</tal:block>
 				</tr>
 			</tbody>
+			<tal:block tal:condition="cur_sys">
+				<tal:block
+					tal:define="x python: request.SESSION.delete('add_cluster_initial')" />
+			</tal:block>
 		</table>
 
 		<input type="hidden" name="numStorage" value="1" />
 
 		<div class="hbSubmit" id="hbSubmit">
-			<input type="button" name="Submit" value="Submit" onClick="validateForm(document.adminform);" />
+			<input type="button" name="Submit" value="Submit"
+				onClick="validateForm(this.form)" />
 		</div>
 	</form>
 </div>
--- conga/luci/homebase/homebase_common.js	2006/11/03 19:13:57	1.14
+++ conga/luci/homebase/homebase_common.js	2006/12/21 05:08:48	1.15
@@ -139,22 +139,40 @@
 	var num_systems = form.numStorage.value;
 
 	var state = cb.checked;
-	var passwd = document.getElementById('__SYSTEM0:Passwd');
-	if (!passwd || passwd.type != 'password')
+
+	var first_passwd = null;
+	var first_system = 0;
+	for (var i = 0 ; i < num_systems ; i++) {
+		var passwd = document.getElementById('__SYSTEM' + i + ':Passwd');
+		if (!passwd || passwd.type != 'password')
+			continue
+		first_passwd = passwd.value;
+		first_system = i;
+		break;
+	}
+
+	if (first_passwd === null)
 		return (-1);
-	passwd = passwd.value;
-	if (!passwd || !state)
-		passwd = '';
 
-	for (var i = 1 ; i < num_systems ; i++) {
+	if (!first_passwd || !state)
+		first_passwd = '';
+
+	for (var i = first_system + 1 ; i < num_systems ; i++) {
 		var element = document.getElementById('__SYSTEM' + i + ':Passwd');
 		if (element && element.type == 'password') {
-			element.value = passwd;
+			element.value = first_passwd;
 			element.disabled = state;
 		}
 	}
 }
 
+function delete_element_id(id_str) {
+	var elem = document.getElementById(id_str);
+	if (!elem || !elem.parentNode)
+		return (-1);
+	elem.parentNode.removeChild(elem);
+}
+
 function pwd0Change(form) {
 	var element = document.getElementById('allSameCheckBox');
 	if (element && element.checked)
@@ -182,23 +200,43 @@
 	newsysp.setAttribute('value', '');
 	newsysp.setAttribute('autocomplete', 'off');
 
+	var first_passwd = '';
+	for (var i = 0 ; i < num_systems - 1 ; i++) {
+		var pwd = document.getElementById('__SYSTEM' + i + ':Passwd');
+		if (!pwd || pwd.type != 'password')
+			continue;
+		first_passwd = pwd.value;
+		break;
+	}
+
 	var allSameCB = document.getElementById('allSameCheckBox');
 	if (allSameCB && allSameCB.checked) {
-		newsysp.setAttribute('value', document.getElementById('__SYSTEM0:Passwd').value);
+		newsysp.setAttribute('value', first_passwd);
 		newsysp.setAttribute('disabled', true);
 	}
 
 	var newrow = document.createElement('tr');
+	newrow.setAttribute('id', '__SYSTEM_ROW_' + num_systems);
 	newrow.className = 'systemsTable';
+
 	var hcol = document.createElement('td');
 	hcol.className = 'systemsTable';
 	var pcol = document.createElement('td');
 	pcol.className = 'systemsTable';
+	var dcol = document.createElement('td');
+	dcol.className = 'systemsTable';
+	var del_img = document.createElement('img');
+	del_img.src = '/luci/delete-row.png';
+	del_img.title = 'delete this row'
+	del_img.className = 'deleteRow'
+	del_img.setAttribute('onClick', 'delete_element_id(\'' + newrow.id + '\')');
+	dcol.appendChild(del_img);
 
 	hcol.appendChild(newsys);
 	pcol.appendChild(newsysp);
 	newrow.appendChild(hcol);
 	newrow.appendChild(pcol);
+	newrow.appendChild(dcol);
 	sltab.appendChild(newrow);
 
 	form.numStorage.value = ++num_systems;
--- conga/luci/homebase/index_html	2006/11/01 23:04:17	1.20
+++ conga/luci/homebase/index_html	2006/12/21 05:08:48	1.21
@@ -134,23 +134,24 @@
 
 		<div class="retmsgs" id="retmsgsdiv" tal:condition="python:(ret and 'messages' in ret and len(ret['messages']))">
 			<div class="hbclosebox">
-				<a href="javascript:hide_element('retmsgsdiv');"><img src="x.png"></a>
+				<a href="javascript:hide_element('retmsgsdiv');"><img src="x.png" class="closeBox" title="dismiss"></a>
 			</div>
-			<ul class="retmsgs">
+			<p class="retmsgs">Status messages:</p>
+			<ul class="statusmsg">
 				<tal:block tal:repeat="e python:ret['messages']">
-					<li class="retmsgs" tal:content="python:e" />
+					<li class="statusmsg" tal:content="python:e" />
 				</tal:block>
 			</ul>
 		</div>
 
 		<div id="errmsgsdiv" class="errmsgs" tal:condition="python:(ret and 'errors' in ret and len(ret['errors']))">
 			<div class="hbclosebox">
-				<a class="hbclosebox" href="javascript:hide_element('errmsgsdiv');"><img src="x.png"></a>
+				<a class="hbclosebox" href="javascript:hide_element('errmsgsdiv');"><img src="x.png" class="closeBox" title="dismiss"></a>
 			</div>
 			<p class="errmsgs">The following errors occurred:</p>
-			<ul class="errmsgs">
+			<ul class="statusmsg">
 				<tal:block tal:repeat="e python:ret['errors']">
-					<li class="errmsgs" tal:content="python:e" />
+					<li class="statusmsg" tal:content="python:e" />
 				</tal:block>
 			</ul>
 		</div>
--- conga/luci/homebase/luci_homebase.css	2006/12/01 14:56:54	1.30
+++ conga/luci/homebase/luci_homebase.css	2006/12/21 05:08:48	1.31
@@ -48,6 +48,8 @@
 }
 
 input.qdisk {
+	font-family: "Bitstream Vera Sans Mono", "DejaVu Sans Mono", monospace ! important;
+	font-size: 12px ! important;
 	padding: .2em;
 }
 
@@ -81,6 +83,10 @@
 	margin-left: 0 ! important;
 }
 
+ul.statusmsg, li.statusmsg {
+	color: black ! important;
+}
+
 ul.deploy {
 	margin-bottom: +.5em;
 }
@@ -153,7 +159,7 @@
 	color: green !important;
 }
 
-p.errmsgs {
+p.errmsgs, p.retmsgs {
 	font-weight: 800;
 }
 
@@ -163,7 +169,7 @@
 	border-width: 2px;
 	border-color: red;
 	margin-top: 2em;
-	max-width: 700px;
+	max-width: 600px ! important;
 }
 
 div.retmsgs {
@@ -172,7 +178,7 @@
 	border-style: dotted;
 	border-width: 2px;
 	border-color: green;
-	max-width: 700px;
+	max-width: 600px ! important;
 }
 
 div.hbCSystems {
@@ -208,6 +214,7 @@
 table.systemsTable {
 	padding-left: +.5em;
 	background: #dee7ec;
+	max-width: 700px;
 }
 
 td.systemsTable {
@@ -250,8 +257,6 @@
 }
 
 img.qdel_img {
-	height: 7px;
-	width: 7px;
 	background: #dee7ec;
 	border: none;
 }
@@ -415,6 +420,10 @@
 	color: blue ! important;
 }
 
+img.deleteRow, img.closeBox {
+	cursor: pointer;
+}
+
 *.running,
 *.node_active {
 	color: green ! important;
--- conga/luci/homebase/validate_sys_remove.js	2006/10/16 20:46:46	1.2
+++ conga/luci/homebase/validate_sys_remove.js	2006/12/21 05:08:48	1.3
@@ -6,7 +6,7 @@
 	if (!form)
 		return (-1);
 
-	var num_clusters = document.getElementById('numClusters').value;
+	var num_clusters = document.getElementById('num_clusters').value;
 	for (var i = 0 ; i < num_clusters ; i++) {
 		var element = document.getElementById('__CLUSTER' + i);
 		if (!element || !element.value || !element.checked)
@@ -14,7 +14,7 @@
 		selected_clusters.push(element.value);
 	}
 
-	var num_storage = document.getElementById('numStorage').value;
+	var num_storage = document.getElementById('num_storage').value;
 	for (var i = 0 ; i < num_storage ; i++) {
 		var element = document.getElementById('__SYSTEM' + i);
 		if (!element || !element.value || !element.checked)
--- conga/luci/site/luci/Extensions/cluster_adapters.py	2006/12/20 22:06:49	1.190
+++ conga/luci/site/luci/Extensions/cluster_adapters.py	2006/12/21 05:08:49	1.191
@@ -5,7 +5,6 @@
 from conga_constants import *
 from ricci_bridge import *
 from ricci_communicator import RicciCommunicator, RicciError, batch_status, extract_module_status
-from string import lower
 import time
 import Products.ManagedSystem
 from Products.Archetypes.utils import make_uuid
@@ -24,9 +23,9 @@
 from QuorumD import QuorumD
 from Heuristic import Heuristic
 from clusterOS import resolveOSType
-from FenceHandler import validateNewFenceDevice, FENCE_OPTS
+from FenceHandler import validateNewFenceDevice, FENCE_OPTS, validateFenceDevice
 from GeneralError import GeneralError
-from homebase_adapters import nodeUnauth, nodeAuth, manageCluster, createClusterSystems, havePermCreateCluster, setNodeFlag, delNodeFlag, userAuthenticated, getStorageNode, getClusterNode, delCluster
+from homebase_adapters import manageCluster, createClusterSystems, havePermCreateCluster, setNodeFlag, delNodeFlag, userAuthenticated, getStorageNode, getClusterNode, delCluster, parseHostForm
 from LuciSyslog import LuciSyslog
 
 #Policy for showing the cluster chooser menu:
@@ -42,391 +41,519 @@
 except:
 	pass
 
-def validateClusterNodes(request, sessionData, clusterName, numStorage):
-	nodeList = list()
-	nodeHash = {}
-	rnodeHash = {}
-	oldNodeHash = {}
-	oldRnodeHash = {}
-	requestResults = {}
-	errors = list()
+def buildClusterCreateFlags(self, batch_map, clusterName):
+	path = str(CLUSTER_FOLDER_PATH + clusterName)
 
-	if sessionData and 'requestResults' in sessionData:
-		requestResults = sessionData['requestResults']
-		if 'nodeHash' in requestResults:
-			oldNodeHash = requestResults['nodeHash']
-		if 'rnodeHash' in requestResults:
-			oldRnodeHash = requestResults['rnodeHash']
+	try:
+		clusterfolder = self.restrictedTraverse(path)
+	except Exception, e:
+		luci_log.debug_verbose('buildCCF0: no cluster folder at %s' % path)
+		return None
 
-	i = 0
-	while i < numStorage:
+	for key in batch_map.keys():
 		try:
-			sysData = request.form['__SYSTEM' + str(i)]
-			if not sysData or sysData[0] == '':
-				raise
-
-			if len(sysData) < 2 or sysData[1] == '':
-				errors.append('No password was specified for host \"' + sysData[0] + '\"')
-				raise
-		except:
-			i += 1
-			continue
-
-		if len(sysData) > 1:
-			node = nodeAuth(None, sysData[0], sysData[1])
-
-			if oldRnodeHash and node['ricci_host'] in oldRnodeHash:
-				oldNode = oldRnodeHash[node['ricci_host']]
-			elif oldNodeHash and node['host'] in nodeHash:
-				oldNode = oldNodeHash[node['host']]
-			else:
-				oldNode = None
-
-			if 'errors' in node:
-				errors.append(node['errors'])
-				node['errors'] = True
-
-			if node['host'] in nodeHash or node['ricci_host'] in rnodeHash:
-				node['errors'] = True
-				errors.append('You added the node \"' + node['host'] + '\" more than once')
-			else:
-				if oldNode and 'prev_auth' in oldNode:
-					node['prev_auth'] = oldNode['prev_auth']
-
-				nodeHash[node['host']] = node
-				rnodeHash[node['ricci_host']] = node
-				nodeList.append(node)
-		i += 1
-
-	sfn = lambda x, y: \
-		x['cur_auth'] - y['cur_auth'] or (('errors' in y) - ('errors' in x))
-	nodeList.sort(sfn)
-
-	dfn = lambda x: not 'cur_auth' in x or x['cur_auth'] != True
-	cluster_properties = {
-		'clusterName': clusterName,
-		'nodeList': nodeList,
-		'nodeHash': nodeHash,
-		'rnodeHash': rnodeHash,
-		'isComplete': len(errors) < 1 and len(filter(dfn, nodeList)) == 0
-	}
-
-	return [errors, cluster_properties]
-
+			key = str(key)
+			batch_id = str(batch_map[key])
+			#This suffix needed to avoid name collision
+			objname = str(key + "____flag")
 
-def validateCreateCluster(self, request):
-	errors = list()
-	requestResults = {}
+			clusterfolder.manage_addProduct['ManagedSystem'].addManagedSystem(objname)
+			#now designate this new object properly
+			objpath = str(path + "/" + objname)
+			flag = self.restrictedTraverse(objpath)
 
-	if not havePermCreateCluster(self):
-		return (False, {'errors': ['You do not have sufficient rights to create a cluster.']})
+			flag.manage_addProperty(BATCH_ID, batch_id, "string")
+			flag.manage_addProperty(TASKTYPE, CLUSTER_ADD, "string")
+			flag.manage_addProperty(FLAG_DESC, "Creating node " + key + " for cluster " + clusterName, "string")
+			flag.manage_addProperty(LAST_STATUS, 0, "int")
+		except Exception, e:
+			luci_log.debug_verbose('buildCCF1: error creating flag for %s: %s' \
+				% (key, str(e)))
 
+def parseClusterNodes(self, request, cluster_os):
+	check_certs = False
 	try:
-	 	sessionData = request.SESSION.get('checkRet')
+		check_certs = 'check_certs' in request.form
 	except:
-		sessionData = None
-
-	if not 'clusterName' in request.form or not request.form['clusterName']:
-		return (False, {'errors': [ 'No cluster name was specified.' ]})
-	clusterName = request.form['clusterName']
+		check_certs = False
 
+	download_pkgs = 1
 	try:
-		numStorage = int(request.form['numStorage'])
+		download_pkgs = int(request.form['download_pkgs'].strip())
 	except:
-		return (False, { 'errors': ['Unknown number of systems entered'], 'requestResults': requestResults })
-
-	if numStorage < 1:
-		return (False, { 'errors': ['A cluster must contain at least one node'], 'requestResults': requestResults })
-
-	ret = validateClusterNodes(request, sessionData, clusterName, numStorage)
-	errors.extend(ret[0])
-	cluster_properties = ret[1]
+		download_pkgs = 1
 
-	rhn_dl = 1
+	clusterName = None
 	try:
-		rhn_dls = request.form['rhn_dl'].strip().lower()
-		if rhn_dls != '1' and rhn_dls != 'true':
-			rhn_dl = 0
+		clusterName = str(request.form['clusterName'])
 	except:
-		rhn_dl = 0
+		clusterName = None
+
+	if clusterName is None:
+		luci_log.debug_verbose('PCN0: no cluster name was given')
+		return (False, { 'errors': [ 'No cluster name was given.' ]})
 
-	enable_storage = 0
+	shared_storage = False
 	try:
-		enable_storage_str = request.form['enable_storage'].strip().lower()
-		if enable_storage_str:
-			enable_storage = 1
+		shared_storage = request.form.has_key('enable_storage')
 	except:
-		enable_storage = 0
+		shared_storage = False
 
+	same_node_passwds = False
 	try:
-		nodeList = cluster_properties['nodeList']
-		if len(nodeList) < 1:
-			raise
+		same_node_passwds = 'allSameCheckBox' in request.form
 	except:
-		errors.append('A cluster must contain at least one node')
+		same_node_passwds = False
 
-	cluster_os = None
-	try:
-		cluster_os = nodeList[0]['os']
-		if not cluster_os:
-			raise KeyError('OS for ' + nodeList[0]['host'] + ' is blank')
-	except KeyError, e:
-		cluster_properties['isComplete'] = False
-		errors.append('Unable to identify the operating system running on the first cluster node: ' + str(e))
+	add_cluster = { 'name': clusterName,
+					'shared_storage': shared_storage,
+					'download_pkgs': download_pkgs,
+					'cluster_os': cluster_os,
+					'identical_passwds': same_node_passwds,
+					'check_certs': check_certs }
+
+	system_list, incomplete, errors, messages = parseHostForm(request, check_certs)
+	add_cluster['nodes'] = system_list
+	
+	for i in system_list:
+		cur_system = system_list[i]
+
+		cur_host_trusted = 'trusted' in cur_system
+		cur_host = cur_system['host']
 
-	if cluster_properties['isComplete'] != True:
-		nodeUnauth(nodeList)
-		return (False, {'errors': errors, 'requestResults':cluster_properties })
-	else:
 		try:
-			if len(filter(lambda x: x['os'] != cluster_os, nodeList[1:])) > 0:
-				raise Exception('different operating systems were detected.')
+			cur_passwd = cur_system['passwd']
 		except:
-			cluster_properties['isComplete'] = False
-			errors.append('Cluster nodes must be running compatible operating systems.')
+			cur_passwd = None
 
-	if cluster_properties['isComplete'] == True:
-		batchNode = createClusterBatch(cluster_os,
-						clusterName,
-						clusterName,
-						map(lambda x: x['host'], nodeList),
-						True,
-						True,
-						enable_storage,
-						False,
-						rhn_dl)
-
-		if not batchNode:
-			nodeUnauth(nodeList)
-			cluster_properties['isComplete'] = False
-			errors.append('Unable to generate cluster creation ricci command')
-			return (False, {'errors': errors, 'requestResults':cluster_properties })
-
-		error = manageCluster(self, clusterName, nodeList)
-		if error:
-			nodeUnauth(nodeList)
-			cluster_properties['isComplete'] = False
-			errors.append(error)
-			return (False, {'errors': errors, 'requestResults':cluster_properties })
-
-		batch_id_map = {}
-		rc = None
-		for i in nodeList:
-			success = True
+		if (cur_host_trusted or not check_certs) and cur_passwd:
 			try:
-				rc = RicciCommunicator(i['host'])
-			except RicciError, e:
-				luci_log.debug('Unable to connect to the ricci agent on %s: %s'\
-					% (i['host'], str(e)))
-				success = False
+				rc = RicciCommunicator(cur_host, enforce_trust=True)
+				if not rc:
+					raise Exception, 'connection failed'
+			except Exception, e:
+				cur_system['errors'] = True
+				incomplete = True
+				errors.append('Unable to connect to %s: %s' \
+					% (cur_host, str(e)))
+				luci_log.debug_verbose('PCN1: %s: %s' % (cur_host, str(e)))
+				continue
+
+			prev_auth = rc.authed()
+			cur_system['prev_auth'] = prev_auth
+
+			try:
+				if prev_auth:
+					messages.append('Host %s is already authenticated.' \
+						% cur_host) 
+				else:
+					rc.auth(cur_passwd)
+
+				if not rc.authed():
+					raise Exception, 'authentication failed'
 			except:
-				success = False
+				cur_system['errors'] = True
+				incomplete = True
+				errors.append('Error authenticating to %s: %s' \
+					% (cur_host, str(e)))
+				luci_log.debug_verbose('PCN2: %s: %s' % (cur_host, str(e)))
+				continue
+
+			cur_cluster_info = rc.cluster_info()
+			if cur_cluster_info[0] or cur_cluster_info[1]:
+				cur_system['errors'] = True
+				incomplete = True
+
+				if cur_cluster_info[0]:
+					cur_cluster_name = cur_cluster_info[0]
+				elif cur_cluster_info[1]:
+					cur_cluster_name = cur_cluster_info[1]
 
-			if success == True:
 				try:
-					resultNode = rc.process_batch(batchNode, async=True)
-					batch_id_map[i['host']] = resultNode.getAttribute('batch_id')
-				except:
-					success = False
+					if not cur_system['prev_auth']:
+						rc.unauth()
+						del cur_system['trusted']
+				except Exception, e:
+					luci_log.debug_verbose('PCN3: %s: %s' % (cur_host, str(e)))
+
+				errors.append('%s reports it is a member of cluster \"%s\"' \
+					% (cur_host, cur_cluster_name))
+				luci_log.debug_verbose('PCN4: %s: already in %s cluster' \
+					% (cur_host, cur_cluster_name))
+				continue
 
-			if not success:
-				nodeUnauth(nodeList)
-				cluster_properties['isComplete'] = False
-				errors.append('An error occurred while attempting to add cluster node \"' + i['host'] + '\"')
-				return (False, {'errors': errors, 'requestResults':cluster_properties })
-		buildClusterCreateFlags(self, batch_id_map, clusterName)
+			cur_host_os = resolveOSType(rc.os())
+			if cluster_os is None:
+				cluster_os = cur_host_os
+				add_cluster['cluster_os'] = cur_host_os
+
+			elif cluster_os != cur_host_os:
+				cur_system['errors'] = True
+				incomplete = True
 
-	response = request.RESPONSE
-	response.redirect(request['URL'] + "?pagetype=" + CLUSTER_CONFIG + "&clustername=" + clusterName + '&busyfirst=true')
+				try:
+					if not cur_system['prev_auth']:
+						rc.unauth()
+						del cur_system['trusted']
+				except Exception, e:
+					luci_log.debug_verbose('PCN5: %s: %s' % (cur_host, str(e)))
+
+				errors.append('The cluster software version on %s (%s) does not match the software on the other cluster nodes (%s)' % (cur_host, cur_host_os, cluster_os))
+				luci_log.debug_verbose('PCN6: version mismatch for %s: (%s vs. %s)' \
+					% (cur_host, cur_host_os, cluster_os))
+				continue
 
-def buildClusterCreateFlags(self, batch_map, clusterName):
-	path = str(CLUSTER_FOLDER_PATH + clusterName)
+	return add_cluster, incomplete, errors, messages
 
+def validateCreateCluster(self, request):
 	try:
-		clusterfolder = self.restrictedTraverse(path)
-	except Exception, e:
-		luci_log.debug_verbose('buildCCF0: no cluster folder at %s' % path)
-		return None
+		request.SESSION.delete('create_cluster')
+	except:
+		pass
 
-	for key in batch_map.keys():
-		try:
-			key = str(key)
-			batch_id = str(batch_map[key])
-			#This suffix needed to avoid name collision
-			objname = str(key + "____flag")
+	cluster_os = None
+	try:
+		cluster_os = request.form['cluster_os'].strip()
+		if not cluster_os:
+			raise Exception, 'cluster OS is blank'
+	except:
+		cluster_os = None
 
-			clusterfolder.manage_addProduct['ManagedSystem'].addManagedSystem(objname)
-			#now designate this new object properly
-			objpath = str(path + "/" + objname)
-			flag = self.restrictedTraverse(objpath)
+	add_cluster, incomplete, errors, messages = parseClusterNodes(self, request, cluster_os)
+	clusterName = add_cluster['name']
 
-			flag.manage_addProperty(BATCH_ID, batch_id, "string")
-			flag.manage_addProperty(TASKTYPE, CLUSTER_ADD, "string")
-			flag.manage_addProperty(FLAG_DESC, "Creating node " + key + " for cluster " + clusterName, "string")
-			flag.manage_addProperty(LAST_STATUS, 0, "int")
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('create_cluster', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
+
+	node_list = add_cluster['nodes'].keys()
+	batchNode = createClusterBatch(add_cluster['cluster_os'],
+					clusterName,
+					clusterName,
+					node_list,
+					True,
+					True,
+					add_cluster['shared_storage'],
+					False,
+					add_cluster['download_pkgs'])
+
+	if not batchNode:
+		request.SESSION.set('create_cluster', add_cluster)
+		errors.append('Unable to generate cluster creation ricci command')
+		return (False, { 'errors': errors, 'messages': messages })
+
+	error = manageCluster(self, clusterName, add_cluster['nodes'], add_cluster['cluster_os'])
+	if error:
+		errors.append('Unable to create the cluster Luci database objects')
+		request.SESSION.set('create_cluster', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
+
+	batch_id_map = {}
+	for i in node_list:
+		try:
+			rc = RicciCommunicator(i)
+			if not rc:
+				raise 'rc is None'
 		except Exception, e:
-			luci_log.debug_verbose('buildCCF1: error creating flag for %s: %s' \
-				% (key, str(e)))
+			msg = 'Unable to connect to the ricci agent on %s: %s' % (i, str(e))
+			errors.append(msg)
+			luci_log.debug_verbose(msg)
+			if len(batch_id_map) == 0:
+				request.SESSION.set('create_cluster', add_cluster)
+				return (False, { 'errors': errors, 'messages': messages })
+			continue
 
-def validateAddClusterNode(self, request):
-	requestResults = {}
-	errors = list()
+		try:
+			resultNode = rc.process_batch(batchNode, async=True)
+			batch_id_map[i] = resultNode.getAttribute('batch_id')
+		except:
+			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)
+				return (False, { 'errors': errors, 'messages': messages })
+			continue
 
+	buildClusterCreateFlags(self, batch_id_map, clusterName)
+	response = request.RESPONSE
+	response.redirect(request['URL'] + "?pagetype=" + CLUSTER_CONFIG + "&clustername=" + clusterName + '&busyfirst=true')
+
+def validateAddClusterNode(self, request):
 	try:
-		sessionData = request.SESSION.get('checkRet')
+		request.SESSION.delete('add_node')
 	except:
-		sessionData = None
+		pass
 
-	if 'clusterName' in request.form:
-		clusterName = str(request.form['clusterName'])
-	else:
-		luci_log.debug_verbose('vACN00: no cluster name was given')
-		return (False, {'errors': [ 'Cluster name is missing'], 'requestResults': requestResults })
+	check_certs = False
+	try:
+		check_certs = 'check_certs' in request.form
+	except:
+		check_certs = False
 
-	rhn_dl = 1
+	download_pkgs = 1
 	try:
-		rhn_dls = request.form['rhn_dl'].strip().lower()
-		if rhn_dls != '1' and rhn_dls != 'true':
-			rhn_dl = 0
+		download_pkgs = int(request.form['download_pkgs'].strip())
 	except:
-		rhn_dl = 0
+		download_pkgs = 1
 
-	enable_storage = 0
+	cluster_os = None
 	try:
-		enable_storages = request.form['enable_storage'].strip().lower()
-		if enable_storages:
-			enable_storage = 1
+		cluster_os = request.form['cluster_os'].strip()
+		if not cluster_os:
+			raise Exception, 'cluster OS is blank'
 	except:
-		enable_storage = 0
+		cluster_os = None
 
+	clusterName = None
 	try:
-		numStorage = int(request.form['numStorage'])
-		if numStorage < 1:
-			raise Exception, 'no nodes were added'
-	except Exception, e:
-		luci_log.debug_verbose('vACN0: %s: %s' % (clusterName, str(e)))
-		errors.append('You must specify at least one node to add to the cluster')
-		return (False, {'errors': [ errors ], 'requestResults': requestResults })
+		clusterName = str(request.form['clusterName'])
+	except:
+		clusterName = None
+
+	if clusterName is None:
+		luci_log.debug_verbose('VACN0: no cluster name was given')
+		return (False, { 'errors': [ 'No cluster name was given.' ]})
+
+	if cluster_os is None:
+		cluster_folder = None
+		try:
+			cluster_folder = self.restrictedTraverse(str(CLUSTER_FOLDER_PATH + clusterName))
+			if not cluster_folder:
+				raise Exception, 'cluster DB object is missing'
+		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:
+				raise Exception, 'cluster os is blank'
+		except Exception, e:
+			luci_log.debug_verbose('VACN2: %s: %s' % (clusterName, str(e)))
+			cluster_os = None
 
-	ret = validateClusterNodes(request, sessionData, clusterName, numStorage)
-	errors.extend(ret[0])
-	cluster_properties = ret[1]
+		if cluster_os is None:
+			try:
+				cluster_ricci = getRicciAgent(self, clusterName)
+				cluster_os = resolveOSType(cluster_ricci.os())
+			except Exception, e:
+				luci_log.debug_verbose('VACN3: %s: %s' % (clusterName, str(e)))
+				cluster_os = None
+
+	if cluster_os is None:
+		luci_log.debug_verbose('Unable to determine cluster OS for %s' % clusterName)
+		return (False, { 'errors': [ 'Unable to determine the version of the cluster suite this cluster is running.' ] })
 
+	shared_storage = False
 	try:
-		nodeList = cluster_properties['nodeList']
-		if len(nodeList) < 1:
-			raise Exception, 'no cluster nodes'
-	except Exception, e:
-		luci_log.debug_verbose('vACN1: %s: %s' % (clusterName, str(e)))
-		errors.append('You must specify at least one valid node to add to the cluster')
+		shared_storage = request.form.has_key('enable_storage')
+	except:
+		shared_storage = False
 
-	clusterObj = None
+	same_node_passwds = False
 	try:
-		clusterObj = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName)
-		cluster_os = clusterObj.manage_getProperty('cluster_os')
-		if not cluster_os:
-			raise Exception, 'no cluster OS was found in DB for %s' % clusterName
-	except Exception, e:
-		luci_log.debug_verbose('vACN2: %s: %s' % (clusterName, str(e)))
+		same_node_passwds = 'allSameCheckBox' in request.form
+	except:
+		same_node_passwds = False
+
+	add_cluster = { 'name': clusterName,
+					'shared_storage': shared_storage,
+					'download_pkgs': download_pkgs,
+					'cluster_os': cluster_os,
+					'identical_passwds': same_node_passwds,
+					'check_certs': check_certs }
+
+	system_list, incomplete, errors, messages = parseHostForm(request, check_certs)
+	add_cluster['nodes'] = system_list
+	
+	for i in system_list:
+		cur_system = system_list[i]
+
+		cur_host_trusted = 'trusted' in cur_system
+		cur_host = cur_system['host']
+
 		try:
-			cluster_ricci = getRicciAgent(self, clusterName)
-			if not cluster_ricci:
-				raise Exception, 'cannot find a ricci agent for %s' % clusterName
-			cluster_os = getClusterOS(self, cluster_ricci)['os']
-			if clusterObj is None:
-				try:
-					clusterObj = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName)
-				except:
-					pass
+			cur_passwd = cur_system['passwd']
+		except:
+			cur_passwd = None
+
+		if (cur_host_trusted or not check_certs) and cur_passwd:
+			try:
+				rc = RicciCommunicator(cur_host, enforce_trust=True)
+				if not rc:
+					raise Exception, 'connection failed'
+			except Exception, e:
+				cur_system['errors'] = True
+				incomplete = True
+				errors.append('Unable to connect to %s: %s' \
+					% (cur_host, str(e)))
+				luci_log.debug_verbose('VACN4: %s: %s' % (cur_host, str(e)))
+				continue
+
+			prev_auth = rc.authed()
+			cur_system['prev_auth'] = prev_auth
+			try:
+				if prev_auth:
+					messages.append('Host %s is already authenticated.' \
+						% cur_host) 
+				else:
+					rc.auth(cur_passwd)
+
+				if not rc.authed():
+					raise Exception, 'authentication failed'
+			except:
+				cur_system['errors'] = True
+				incomplete = True
+				errors.append('Error authenticating to %s: %s' \
+					% (cur_host, str(e)))
+				luci_log.debug_verbose('VACN5: %s: %s' % (cur_host, str(e)))
+				continue
+
+			cur_cluster_info = rc.cluster_info()
+			if cur_cluster_info[0] or cur_cluster_info[1]:
+				cur_system['errors'] = True
+				incomplete = True
+
+				if cur_cluster_info[0]:
+					cur_cluster_name = cur_cluster_info[0]
+				elif cur_cluster_info[1]:
+					cur_cluster_name = cur_cluster_info[1]
 
 				try:
-					clusterObj.manage_addProperty('cluster_os', cluster_os, 'string')
-				except:
-					pass
-		except Exception, e:
-			luci_log.debug_verbose('vACN3: %s: %s' % (clusterName, str(e)))
-			nodeUnauth(nodeList)
-			cluster_os = None
-			cluster_properties['isComplete'] = False
-			errors.append('Unable to determine the cluster OS for the ' + clusterName + ' cluster.')
+					if not cur_system['prev_auth']:
+						rc.unauth()
+						del cur_system['trusted']
+				except Exception, e:
+					luci_log.debug_verbose('VACN6: %s: %s' % (cur_host, str(e)))
+
+				errors.append('%s reports it is already a member of cluster \"%s\"' % (cur_host, cur_cluster_name))
+				luci_log.debug_verbose('VACN7: %s: already in %s cluster' \
+					% (cur_host, cur_cluster_name))
+				continue
 
-	try:
-		if cluster_os is None:
-			raise Exception, 'no cluster OS found for %s' % clusterName
-		if len(filter(lambda x: x['os'] != cluster_os, nodeList)) > 0:
-			raise Exception, 'different operating systems were detected.'
-	except Exception, e:
-		luci_log.debug_verbose('vACN4: %s: %s' % (clusterName, str(e)))
-		nodeUnauth(nodeList)
-		cluster_properties['isComplete'] = False
-		errors.append('Cluster nodes must be running compatible operating systems.')
-
-	if not cluster_properties['isComplete']:
-		return (False, {'errors': errors, 'requestResults': cluster_properties})
-
-	for clunode in nodeList:
-		try:
-			batchNode = addClusterNodeBatch(clunode['os'],
-							clusterName,
-							True,
-							True,
-							enable_storage,
-							False,
-							rhn_dl)
-			if not batchNode:
-				raise Exception, 'batchnode is None'
-			clunode['batchnode'] = batchNode
-		except Exception, e:
-			luci_log.debug_verbose('vACN5: node add for %s failed: %s' \
-				% (clunode['host'], str(e)))
-			clunode['errors'] = True
-			nodeUnauth(nodeList)
-			cluster_properties['isComplete'] = False
-			errors.append('Unable to initiate node creation for host \"' + clunode['host'] + '\"')
+			cur_host_os = resolveOSType(rc.os())
+			if cluster_os is not None and cluster_os != cur_host_os:
+				cur_system['errors'] = True
+				incomplete = True
+
+				try:
+					if not cur_system['prev_auth']:
+						rc.unauth()
+						del cur_system['trusted']
+				except Exception, e:
+					luci_log.debug_verbose('VACN8: %s: %s' % (cur_host, str(e)))
+
+				errors.append('The cluster software version on %s (%s) does not match the software on the other cluster nodes (%s)' % (cur_host, cur_host_os, cluster_os))
+				luci_log.debug_verbose('VACN9: version mismatch for %s: (%s vs. %s)' \
+					% (cur_host, cur_host_os, cluster_os))
+				continue
 
-	if not cluster_properties['isComplete']:
-		return (False, {'errors': errors, 'requestResults': cluster_properties})
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('add_node', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
 
 	try:
 		cluster_ricci = getRicciAgent(self, clusterName)
 		if not cluster_ricci:
-			raise Exception, 'Unable to get a ricci agent for %s' % clusterName
+			raise Exception, 'Unable to find a ricci agent for %s' % clusterName
 	except Exception, e:
-		cluster_properties['isComplete'] = False
-		nodeUnauth(nodeList)
-		errors.append('Unable to contact a Ricci agent for %s.' % clusterName)
-		luci_log.debug_verbose('vACN6: ricci %s: %s' % (clusterName, str(e)))
-		return (False, {'errors': errors, 'requestResults': cluster_properties})
+		incomplete = True
+		errors.append('Unable to contact a ricci agent for %s.' % clusterName)
+		luci_log.debug_verbose('VACN10: %s: %s' % (clusterName, str(e)))
+
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('add_node', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
 
 	try:
 		model = getModelBuilder(None, cluster_ricci, cluster_ricci.dom0())
 		if not model:
+			errors.append('Unable to build the cluster model for %s' \
+				% clusterName)
 			raise Exception, 'unable to get model for %s' % clusterName
+
 		nodesptr = model.getClusterNodesPtr()
 		used_ids = {}
 		for i in model.getNodes():
+			used_ids[int(i.getAttribute('nodeid'))] = 1
+			node_name = str(i.getAttribute('name'))
+			if node_name in system_list:
+				system_list[node_name]['errors'] = True
+				errors.append('%s is already a member of %s' \
+					% (node_name, clusterName))
+	except Exception, e:
+		incomplete = True
+		errors.append('Unable to build the cluster model for %s' \
+			% clusterName)
+		luci_log.debug_verbose('VACN11: %s' % str(e))
+
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('add_node', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
+
+	next_node_id = 1
+
+	try:
+		for x in system_list:
+			i = system_list[x]
+
 			try:
-				used_ids[int(i.getAttribute('nodeid'))] = 1
+				batch_node = addClusterNodeBatch(cluster_os,
+								clusterName,
+								True,
+								True,
+								shared_storage,
+								False,
+								download_pkgs)
+				if not batch_node:
+					raise Exception, 'batch is blank'
+				system_list[x]['batch'] = batch_node
 			except Exception, e:
-				luci_log.debug_verbose('vACN7: %s' % str(e))
-				pass
-		next_node_id = 1
-		for i in nodeList:
+				cur_system['errors'] = True
+				incomplete = True
+
+				try:
+					if not cur_system['prev_auth']:
+						rc.unauth()
+						del cur_system['trusted']
+				except Exception, e:
+					luci_log.debug_verbose('VACN12: %s: %s' % (cur_host, str(e)))
+
+				errors.append('Unable to initiate cluster join for %s' % cur_host)
+				luci_log.debug_verbose('VACN13: %s: %s' % (cur_host, str(e)))
+				continue
+
 			next_node_id += 1
 			new_node = ClusterNode()
-			new_node.attr_hash['name'] = i['host']
+			new_node.attr_hash['name'] = str(i['host'])
 			new_node.attr_hash['votes'] = str(1)
 			while next_node_id in used_ids:
 				next_node_id += 1
 			new_node.attr_hash['nodeid'] = str(next_node_id)
 			nodesptr.addChild(new_node)
 
-		model.isModified = True
+		if incomplete or len(errors) > 0:
+			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:
-			raise Exception, 'unable to export model as a string'
-		batch_number, result = setClusterConf(cluster_ricci, conf_str)
+			raise Exception, 'Unable to save the new cluster model.'
 
+		batch_number, result = setClusterConf(cluster_ricci, conf_str)
+		if not batch_number or not result:
+			raise Exception, 'batch or result is None'
+	except Exception, e:
+		incomplete = True
+		errors.append('Unable to save the new cluster model.')
+		luci_log.debug_verbose('VACN14: %s' % str(e))
+
+	# Propagate the new cluster.conf to the existing nodes
+	# before having any of the new nodes join. If this fails,
+	# abort the whole process.
+	try:
 		while True:
 			batch_ret = checkBatch(cluster_ricci, batch_number)
 			code = batch_ret[0]
@@ -438,47 +565,61 @@
 			if code == False:
 				time.sleep(0.5)
 	except Exception, e:
-		luci_log.debug_verbose('vACN8: %s' % str(e))
-		errors.append('Unable to update the cluster node list for %s' % clusterName)
-		return (False, {'errors': errors, 'requestResults': cluster_properties})
+		incomplete = True
+		errors.append('Unable to update the cluster node list for %s' \
+			% clusterName)
+		luci_log.debug_verbose('VACN15: %s' % str(e))
+
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('add_node', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
 
-	error = createClusterSystems(self, clusterName, nodeList)
+	error = createClusterSystems(self, clusterName, system_list)
 	if error:
-		luci_log.debug_verbose('vACN9: %s: %s' % (clusterName, str(e)))
-		nodeUnauth(nodeList)
-		cluster_properties['isComplete'] = False
+		incomplete = True
 		errors.append(error)
-		return (False, {'errors': errors, 'requestResults': cluster_properties})
+		luci_log.debug_verbose('VACN16: %s: %s' % (clusterName, error))
+
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('add_node', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
 
 	batch_id_map = {}
-	for clunode in nodeList:
+	for x in system_list:
+		clunode = system_list[x]
 		success = True
+
+		cur_host = clunode['host']
 		try:
-			rc = RicciCommunicator(clunode['host'])
+			rc = RicciCommunicator(cur_host)
 			if not rc:
 				raise Exception, 'rc is None'
 		except Exception, e:
-			nodeUnauth([clunode['host']])
 			success = False
-			luci_log.info('vACN10: Unable to connect to the ricci daemon on host %s: %s' % (clunode['host'], str(e)))
+			clunode['errors'] = True
+			errors.append('Unable to connect to the ricci agent on %s: %s' \
+				% (cur_host, str(e)))
+			luci_log.info('VACN17: Unable to connect to the ricci daemon on host %s: %s' % (clunode['host'], str(e)))
 
 		if success:
 			try:
-				resultNode = rc.process_batch(clunode['batchnode'], async=True)
-				batch_id_map[clunode['host']] = resultNode.getAttribute('batch_id')
+				resultNode = rc.process_batch(clunode['batch'], async=True)
+				batch_id_map[cur_host] = resultNode.getAttribute('batch_id')
 			except Exception, e:
-				nodeUnauth([clunode['host']])
+				clunode['errors'] = True
 				success = False
-				luci_log.info('vACN11: %s' % (clunode['host'], str(e)))
+				luci_log.debug_verbose('VACN18: %s: %s' \
+					% (cur_host, str(e)))
 
 		if not success:
-			cluster_properties['isComplete'] = False
-			errors.append('An error occurred while attempting to add cluster node \"' + clunode['host'] + '\"')
+			incomplete = True
+			errors.append('An error occurred while attempting to add cluster node \"%s\"')
 
-	buildClusterCreateFlags(self, batch_id_map, clusterName)
+	if incomplete or len(errors) > 0:
+		request.SESSION.set('add_node', add_cluster)
+		return (False, { 'errors': errors, 'messages': messages })
 
-	if len(errors) > 0:
-		return (False, {'errors': errors, 'requestResults': cluster_properties})
+	buildClusterCreateFlags(self, batch_id_map, clusterName)
 
 	response = request.RESPONSE
 	response.redirect(request['URL'] + "?pagetype=" + CLUSTER_CONFIG + "&clustername=" + clusterName + '&busyfirst=true')
@@ -642,6 +783,9 @@
 		return (False, {'errors': [ 'Unable to determine cluster name' ]})
 
 	try:
+		cp = model.getClusterPtr()
+		cp.incrementConfigVersion()
+		model.setModified(True)
 		conf = model.exportModelAsString()
 		if not conf:
 			raise Exception, 'model string for %s is blank' % clustername
@@ -696,9 +840,9 @@
 			if res and res[2]:
 				errors.extend(res[2])
 			raise Exception, 'An error occurred while adding this resource'
-		modelb = res[1]
+		model = res[1]
 		newres = res[0]
-		addResource(self, request, modelb, newres, res_type)
+		addResource(self, request, model, newres, res_type)
 	except Exception, e:
 		if len(errors) < 1:
 			errors.append('An error occurred while adding this resource')
@@ -747,7 +891,6 @@
 	try:
 		model.usesMulticast = True
 		model.mcast_address = addr_str
-		model.isModified = True
 	except Exception, e:
 		luci_log.debug('Error updating mcast properties: %s' % str(e))
 		errors.append('Unable to update cluster multicast properties')
@@ -1064,7 +1207,7 @@
   try:
     clustername = model.getClusterName()
     if not clustername:
-      raise Exception, 'cluster name from modelb.getClusterName() is blank'
+      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') 
@@ -1143,7 +1286,7 @@
     return (False, {'errors': ['No form was submitted']})
 
   #fencehandler = FenceHandler()
-  error_code,error_string = validateNewFenceDevice(form, model)
+  error_code, error_string = validateNewFenceDevice(form, model)
   if error_code == FD_VAL_SUCCESS:
     messages.append(error_string)
     try:
@@ -1161,7 +1304,7 @@
     try:
       clustername = model.getClusterName()
       if not clustername:
-        raise Exception, 'cluster name from modelb.getClusterName() is blank'
+        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') 
@@ -1240,7 +1383,7 @@
   #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, error_string = validateFenceDevice(form, model)
   if error_code == FD_VAL_SUCCESS:
     messages.append(error_string)
     try:
@@ -1258,7 +1401,7 @@
     try:
       clustername = model.getClusterName()
       if not clustername:
-        raise Exception, 'cluster name from modelb.getClusterName() is blank'
+        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') 
@@ -1363,7 +1506,7 @@
     error_string = "Fence device %s could not be removed from configuration" % fencedev_name
  
   try:
-    model.removeFenceInstancesForFenceDevice(orig_name)
+    model.removeFenceInstancesForFenceDevice(fencedev_name)
   except:
     luci_log.debug_verbose('VFD: Could not remove fence instances for')
      
@@ -1385,7 +1528,7 @@
     try:
       clustername = model.getClusterName()
       if not clustername:
-        raise Exception, 'cluster name from modelb.getClusterName() is blank'
+        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') 
@@ -2176,7 +2319,7 @@
 
 		return None
 
-	cluname = lower(clustername)
+	cluname = clustername.lower()
 
 	for node in nodes:
 		try:
@@ -2432,7 +2575,7 @@
 			results.append(vals)
 	return results
 
-def getServicesInfo(self, status, modelb, req):
+def getServicesInfo(self, status, model, req):
 	map = {}
 	maplist = list()
 
@@ -2466,7 +2609,7 @@
 			itemmap['cfgurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + SERVICE
 			itemmap['delurl'] = baseurl + "?" + "clustername=" + cluname + "&servicename=" + item['name'] + "&pagetype=" + SERVICE_DELETE
 
-			svc = modelb.retrieveServiceByName(item['name'])
+			svc = model.retrieveServiceByName(item['name'])
 			dom = svc.getAttribute("domain")
 			if dom is not None:
 				itemmap['faildom'] = dom
@@ -2477,7 +2620,7 @@
 	map['services'] = maplist
 	return map
 
-def getServiceInfo(self, status, modelb, req):
+def getServiceInfo(self, status, model, req):
 	#set up struct for service config page
 	hmap = {}
 	root_uuid = 'toplevel'
@@ -2527,7 +2670,7 @@
 					innermap['restarturl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_RESTART
 					innermap['delurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_DELETE
 
-					nodes = modelb.getNodes()
+					nodes = model.getNodes()
 					for node in nodes:
 						starturl = {}
 						if node.getName() != nodename:
@@ -2541,7 +2684,7 @@
 					innermap = {}
 					innermap['current'] = "This service is currently stopped"
 					innermap['enableurl'] = baseurl + "?" + "clustername=" + cluname +"&servicename=" + servicename + "&pagetype=" + SERVICE_START
-					nodes = modelb.getNodes()
+					nodes = model.getNodes()
 					starturls = list()
 					for node in nodes:
 						starturl = {}
@@ -2553,7 +2696,7 @@
 
 	#Now build hashes for resources under service.
 	#first get service by name from model
-	svc = modelb.getService(servicename)
+	svc = model.getService(servicename)
 	resource_list = list()
 	if svc is not None:
 		indent_ctr = 0
@@ -2730,7 +2873,7 @@
 	response = req.RESPONSE
 	response.redirect(req['URL'] + "?pagetype=" + SERVICE_LIST + "&clustername=" + cluname + '&busyfirst=true')
 
-def getFdomsInfo(self, modelb, request, clustatus):
+def getFdomsInfo(self, model, request, clustatus):
   slist = list()
   nlist = list()
   for item in clustatus:
@@ -2741,8 +2884,8 @@
   fdomlist = list()
   clustername = request['clustername']
   baseurl = request['URL']
-  fdoms = modelb.getFailoverDomains()
-  svcs = modelb.getServices()
+  fdoms = model.getFailoverDomains()
+  svcs = model.getServices()
   for fdom in fdoms:
     fdom_map = {}
     fdom_map['name'] = fdom.getName()
@@ -3366,10 +3509,10 @@
 	else:
 		delete_target = None
 		nodelist = model.getNodes()
-		find_node = lower(nodename)
+		find_node = nodename.lower()
 		for n in nodelist:
 			try:
-				if lower(n.getName()) == find_node:
+				if n.getName().lower() == find_node:
 					delete_target = n
 					break
 			except:
@@ -3387,6 +3530,9 @@
 				% (delete_target.getName(), str(e)))
 
 		try:
+			cp = model.getClusterPtr()
+			cp.incrementConfigVersion()
+			model.setModified(True)
 			str_buf = model.exportModelAsString()
 			if not str_buf:
 				raise Exception, 'model string is blank'
@@ -3471,8 +3617,8 @@
 				% (nodename_resolved, clustername))
 			return (False, {'errors': [ 'Node %s reports it is not in a cluster.' % nodename_resolved ]})
 
-		cname = lower(clustername)
-		if cname != lower(cluinfo[0]) and cname != lower(cluinfo[1]):
+		cname = clustername.lower()
+		if cname != cluinfo[0].lower() and cname != cluinfo[1].lower():
 			luci_log.debug('NTP6: node %s in unknown cluster %s:%s (expected %s)' % (nodename_resolved, cluinfo[0], cluinfo[1], clustername))
 			return (False, {'errors': [ 'Node %s reports it in cluster \"%s\". We expect it to be a member of cluster \"%s\"' % (nodename_resolved, cluinfo[0], clustername) ]})
 
@@ -4188,6 +4334,9 @@
     xvm.addAttribute("path", req.form['xenvmpath'])
 
   try:
+    cp = model.getClusterPtr()
+    cp.incrementConfigVersion()
+    model.setModified(True)
     stringbuf = model.exportModelAsString()
     if not stringbuf:
       raise Exception, 'model is blank'
@@ -4198,7 +4347,7 @@
   try:
     clustername = model.getClusterName()
     if not clustername:
-      raise Exception, 'cluster name from modelb.getClusterName() is blank'
+      raise Exception, 'cluster name from model.getClusterName() is blank'
   except Exception, e:
     luci_log.debug_verbose('error: getClusterName: %s' % str(e))
     return None
@@ -4538,7 +4687,7 @@
 		map['isVirtualized'] = False
 	return map
 
-def getResourcesInfo(modelb, request):
+def getResourcesInfo(model, request):
 	resList = list()
 	baseurl = request['URL']
 
@@ -4551,7 +4700,7 @@
 			luci_log.debug_verbose('getResourcesInfo missing cluster name')
 			return resList
 
-	for item in modelb.getResources():
+	for item in model.getResources():
 		itemmap = {}
 		itemmap['name'] = item.getName()
 		itemmap['attrs'] = item.attr_hash
@@ -4562,9 +4711,9 @@
 		resList.append(itemmap)
 	return resList
 
-def getResourceInfo(modelb, request):
-	if not modelb:
-		luci_log.debug_verbose('GRI0: no modelb object in session')
+def getResourceInfo(model, request):
+	if not model:
+		luci_log.debug_verbose('GRI0: no model object in session')
 		return {}
 
 	name = None
@@ -4603,7 +4752,7 @@
 		luci_log.debug_verbose('getResourceInfo missing URL')
 		return {}
 
-	for res in modelb.getResources():
+	for res in model.getResources():
 		if res.getName() == name:
 			try:
 				resMap = {}
@@ -4620,7 +4769,7 @@
 	errstr = 'An error occurred while attempting to set the new cluster.conf'
 
 	try:
-		modelb = request.SESSION.get('model')
+		model = request.SESSION.get('model')
 	except Exception, e:
 		luci_log.debug_verbose('delService0: no model: %s' % str(e))
 		return (False, {'errors': [ errstr ] })
@@ -4665,13 +4814,16 @@
 		return (False, {'errors': [ '%s: unable to find a Ricci agent for this cluster.' % errstr ]})
 
 	try:
-		modelb.deleteService(name)
+		model.deleteService(name)
 	except Exception, e:
 		luci_log.debug_verbose('delService5: Unable to find a service named %s for cluster %s' % (name, clustername))
 		return (False, {'errors': [ '%s: error removing service %s.' % (errstr, name) ]})
 
 	try:
-		conf = modelb.exportModelAsString()
+		cp = model.getClusterPtr()
+		cp.incrementConfigVersion()
+		model.setModified(True)
+		conf = model.exportModelAsString()
 		if not conf:
 			raise Exception, 'model string is blank'
 	except Exception, e:
@@ -4696,7 +4848,7 @@
 	errstr = 'An error occurred while attempting to set the new cluster.conf'
 
 	try:
-		modelb = request.SESSION.get('model')
+		model = request.SESSION.get('model')
 	except Exception, e:
 		luci_log.debug_verbose('delResource0: no model: %s' % str(e))
 		return errstr
@@ -4735,7 +4887,7 @@
 		luci_log.debug_verbose('delResource3: %s: %s' % (errstr, str(e)))
 		return errstr + ': could not determine the ricci agent hostname'
 
-	resPtr = modelb.getResourcesPtr()
+	resPtr = model.getResourcesPtr()
 	resources = resPtr.getChildren()
 
 	found = 0
@@ -4750,7 +4902,10 @@
 		return errstr + ': the specified resource was not found.'
 
 	try:
-		conf = modelb.exportModelAsString()
+		cp = model.getClusterPtr()
+		cp.incrementConfigVersion()
+		model.setModified(True)
+		conf = model.exportModelAsString()
 		if not conf:
 			raise Exception, 'model string is blank'
 	except Exception, e:
@@ -4779,9 +4934,9 @@
 		luci_log.debug_verbose('addIp error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addIp error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addIp error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -4789,7 +4944,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addIp error: %s' % str(e))
 			return None
@@ -4825,7 +4980,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addFs(request, form=None):
 	if form is None:
@@ -4835,9 +4990,9 @@
 		luci_log.debug_verbose('addFs error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addFs error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addFs error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -4845,7 +5000,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addFs error: %s' % str(e))
 			return None
@@ -4929,7 +5084,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addGfs(request, form=None):
 	if form is None:
@@ -4939,9 +5094,9 @@
 		luci_log.debug_verbose('addGfs error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addGfs error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addGfs error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -4949,7 +5104,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 			if not res:
 				luci_log.debug('resource %s was not found for editing' % oldname)
 				return None
@@ -5020,7 +5175,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addNfsm(request, form=None):
 	if form is None:
@@ -5030,9 +5185,9 @@
 		luci_log.debug_verbose('addNfsm error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addNfsm error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addNfsm error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -5040,7 +5195,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addNfsm error: %s' % str(e))
 			return None
@@ -5115,7 +5270,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addNfsc(request, form=None):
 	if form is None:
@@ -5125,9 +5280,9 @@
 		luci_log.debug_verbose('addNfsc error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addNfsc error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addNfsc error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -5135,7 +5290,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addNfsc error: %s' % str(e))
 			return None
@@ -5179,19 +5334,19 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addNfsx(request, form=None):
 	if form is None:
 		form = request.form
 
 	if not form:
-		luci_log.debug_verbose('addNfsx error: modelb is missing')
+		luci_log.debug_verbose('addNfsx error: model is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addNfsx error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addNfsx error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -5199,7 +5354,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addNfsx error: %s', str(e))
 			return None
@@ -5227,7 +5382,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addScr(request, form=None):
 	if form is None:
@@ -5237,9 +5392,9 @@
 		luci_log.debug_verbose('addScr error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addScr error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addScr error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -5247,7 +5402,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addScr error: %s' % str(e))
 			return None
@@ -5285,7 +5440,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 def addSmb(request, form=None):
 	if form is None:
@@ -5295,9 +5450,9 @@
 		luci_log.debug_verbose('addSmb error: form is missing')
 		return None
 
-	modelb = request.SESSION.get('model')
-	if not modelb:
-		luci_log.debug_verbose('addSmb error: modelb is missing')
+	model = request.SESSION.get('model')
+	if not model:
+		luci_log.debug_verbose('addSmb error: model is missing')
 		return None
 
 	if form.has_key('edit'):
@@ -5305,7 +5460,7 @@
 			oldname = form['oldname'].strip()
 			if not oldname:
 				raise KeyError, 'oldname is blank.'
-			res = getResourceForEdit(modelb, oldname)
+			res = getResourceForEdit(model, oldname)
 		except Exception, e:
 			luci_log.debug_verbose('addSmb error: %s' % str(e))
 			return None
@@ -5341,7 +5496,7 @@
 
 	if len(errors) > 1:
 		return [None, None, errors]
-	return [res, modelb, None]
+	return [res, model, None]
 
 resourceAddHandler = {
 	'ip': addIp,
@@ -5354,9 +5509,9 @@
 	'smb': addSmb
 }
 
-def resolveClusterChanges(self, clusterName, modelb):
+def resolveClusterChanges(self, clusterName, model):
 	try:
-		mb_nodes = modelb.getNodes()
+		mb_nodes = model.getNodes()
 		if not mb_nodes or not len(mb_nodes):
 			raise Exception, 'node list is empty'
 	except Exception, e:
@@ -5431,8 +5586,8 @@
 	
 	return messages
 
-def addResource(self, request, modelb, res, res_type):
-	clustername = modelb.getClusterName()
+def addResource(self, request, model, res, res_type):
+	clustername = model.getClusterName()
 	if not clustername:
 		luci_log.debug_verbose('addResource0: no cluname from mb')
 		return 'Unable to determine cluster name'
@@ -5443,13 +5598,16 @@
 		return 'Unable to find a ricci agent for the %s cluster' % clustername
 
 	try:
-		modelb.getResourcesPtr().addChild(res)
+		model.getResourcesPtr().addChild(res)
 	except Exception, e:
 		luci_log.debug_verbose('addResource2: adding the new resource failed: %s' % str(e))
 		return 'Unable to add the new resource'
 
 	try:
-		conf = modelb.exportModelAsString()
+		cp = model.getClusterPtr()
+		cp.incrementConfigVersion()
+		model.setModified(True)
+		conf = model.exportModelAsString()
 		if not conf:
 			raise Exception, 'model string for %s is blank' % clustername
 	except Exception, e:
@@ -5484,8 +5642,8 @@
 	response = request.RESPONSE
 	response.redirect(request['URL'] + "?pagetype=" + RESOURCES + "&clustername=" + clustername + '&busyfirst=true')
 
-def getResource(modelb, name):
-	resPtr = modelb.getResourcesPtr()
+def getResource(model, name):
+	resPtr = model.getResourcesPtr()
 	resources = resPtr.getChildren()
 
 	for res in resources:
@@ -5495,8 +5653,8 @@
 	luci_log.debug_verbose('getResource: unable to find resource \"%s\"' % name)
 	raise KeyError, name
 
-def getResourceForEdit(modelb, name):
-	resPtr = modelb.getResourcesPtr()
+def getResourceForEdit(model, name):
+	resPtr = model.getResourcesPtr()
 	resources = resPtr.getChildren()
 
 	for res in resources:
@@ -5591,18 +5749,18 @@
 		return None
 
 	try:
-		modelb = ModelBuilder(0, None, None, cluster_conf_node)
-		if not modelb:
+		model = ModelBuilder(0, None, None, cluster_conf_node)
+		if not model:
 			raise Exception, 'ModelBuilder returned None'
 	except Exception, e:
 		try:
-			luci_log.debug_verbose('GMB1: An error occurred while trying to get modelb for conf \"%s\": %s' % (cluster_conf_node.toxml(), str(e)))
+			luci_log.debug_verbose('GMB1: An error occurred while trying to get model for conf \"%s\": %s' % (cluster_conf_node.toxml(), str(e)))
 		except:
 			luci_log.debug_verbose('GMB1: ModelBuilder failed')
 
-	if modelb:
-		modelb.setIsVirtualized(isVirtualized)
-	return modelb
+	if model:
+		model.setIsVirtualized(isVirtualized)
+	return model
 
 def getModelForCluster(self, clustername):
 	rc = getRicciAgent(self, clustername)
--- conga/luci/site/luci/Extensions/homebase_adapters.py	2006/12/14 21:37:15	1.41
+++ conga/luci/site/luci/Extensions/homebase_adapters.py	2006/12/21 05:08:49	1.42
@@ -6,7 +6,9 @@
 from conga_constants import PLONE_ROOT, CLUSTER_NODE_NEED_AUTH, \
 							HOMEBASE_ADD_CLUSTER, HOMEBASE_ADD_CLUSTER_INITIAL, \
 							HOMEBASE_ADD_SYSTEM, HOMEBASE_ADD_USER, \
-							HOMEBASE_DEL_SYSTEM, HOMEBASE_DEL_USER, HOMEBASE_PERMS
+							HOMEBASE_DEL_SYSTEM, HOMEBASE_DEL_USER, HOMEBASE_PERMS, \
+							STORAGE_FOLDER_PATH, CLUSTER_FOLDER_PATH
+
 from ricci_bridge import getClusterConf
 from ricci_communicator import RicciCommunicator, CERTS_DIR_PATH
 from clusterOS import resolveOSType
@@ -40,7 +42,7 @@
 			if dsResult:
 				errors.append(dsResult)
 			else:
-				messages.append('Removed storage system \"' + i + '\" successfully')
+				messages.append('Removed storage system \"%s\" successfully' % i)
 
 	if '__CLUSTER' in request.form:
 		cluNames = request.form['__CLUSTER']
@@ -51,7 +53,7 @@
 			if dcResult:
 				errors.append(dcResult)
 			else:
-				messages.append('Removed cluster \"' + i + '\" successfully')
+				messages.append('Removed cluster \"%s\" successfully' % i)
 
 	if len(errors) > 0:
 		retCode = False
@@ -129,369 +131,531 @@
 	messages.append('Added new user \"' + user + '\" successfully')
 	return (True, {'messages': messages, 'params': { 'user': user }})
 
-def nodeUnauth(nodeList):
-	for i in nodeList:
-		try:
-			if i['prev_auth'] != True:
-				host = i['host']
-				rc = RicciCommunicator(host)
-				rc.unauth()
-				i['cur_auth'] = False
-		except Exception, e:
-			try:
-				luci_log.debug_verbose('unauth for %s failed: %s' \
-					% (i['host'], str(e)))
-			except:
-				pass
-
-def nodeAuth(cluster, host, passwd):
+def validateAddClusterInitial(self, request):
+	errors = list()
 	messages = list()
-	systemName = host
-	os_str = 'rhel5'
 
 	try:
-		rc = RicciCommunicator(host)
-		if not rc:
-			luci_log.debug_verbose('nodeAuth0: rc is None')
-			raise Exception, 'unknown error'
-	except Exception, e:
-		try:
-			error = 'Ricci connection to %s failed: %s' % (host, str(e))
-		except:
-			error = 'Ricci connection to %s failed' % host
-		luci_log.debug_verbose('nodeAuth1: rc failed: %s' % error)
-
-		return { 'host': host, 'ricci_host': host, 'errors': error, 'cur_auth': False, 'os': os_str }
-
-	if rc.authed():
-		prevAuth = True
-		messages.append('Luci is already authenticated to %s -- not checking password' % host)
-	else:
-		prevAuth = False
-		if not passwd:
-			return { 'host': host, 'ricci_host': systemName, 'prev_auth': False, 'cur_auth': False, 'os': os_str }
-		else:
-			try:
-				rc.auth(passwd)
-			except:
-				pass
+		request.SESSION.delete('add_cluster_initial')
+		request.SESSION.delete('add_cluster')
+	except:
+		pass
 
-	if rc.authed():
-		try:
-			os_str = resolveOSType(rc.os())
-			if not os_str:
-				raise
-		except:
-			os_str = "rhel5"  #Backup plan in case all is almost lost...
+	cur_host = None
+	try:
+		sysData = request.form['__SYSTEM0']
+		if not sysData or len(sysData) < 1:
+			raise Exception, 'no node was given'
+		cur_host = sysData[0]
+	except Exception, e:
+		luci_log.debug_verbose('vACI0: %s' % str(e))
+		return (False, { 'errors': [ 'You must provide the address of at least one node in the cluster you wish to add.' ]})
+
+	cur_entry = { 'host': cur_host }
+	try:
+		if len(sysData) < 2 or not sysData[1]:
+			raise Exception, 'no password'
+		cur_pass = sysData[1]
+		cur_entry['passwd'] = cur_pass
+	except:
+		luci_log.debug_verbose('vACI1: %s no password given')
+		request.SESSION.set('add_cluster_initial', cur_entry)
+		return (False, { 'errors': [ 'No password was given for %s' % cur_host ] })
 
-		systemName = rc.system_name()
-		if systemName[:9] == 'localhost' or systemName[:5] == '127.0':
-			systemName = host
-		node = { 'host': host, 'ricci_host': systemName, 'prev_auth': prevAuth, 'cur_auth': True, 'os': os_str }
+	check_certs = False
+	try:
+		check_certs = request.form.has_key('check_certs')
+	except:
+		check_certs = False
 
-		cluster_info = rc.cluster_info()
-		if cluster and ((not cluster_info) or (cluster_info[0] != cluster)):
-			node['errors'] = 'Node \"' + host + '\" is reporting it is not a member of cluster \"' + cluster + '\"'
-			if cluster_info and cluster_info[0]:
-				node['errors'] += ' and that it is a member of cluster \"' + cluster_info[0] + '\"'
-		if not cluster and cluster_info and cluster_info[0]:
-			node['errors'] = 'Node \"' + host + '\" reports it is a member of cluster \"' + cluster_info[0] + '\"'
-		return node
+	cur_host_trusted = False
+	try:
+		cur_host_trusted = request.form.has_key('host_is_trusted')
+	except:
+		cur_host_trusted = False
 
-	error = 'Unable to authenticate to the ricci agent on \"' + host + '\"'
-	return { 'host': host, 'ricci_host': systemName, 'prev_auth': False , 'cur_auth': False, 'errors': error, 'os': os_str }
+	cur_host_fp = None
+	try:
+		cur_host_fp = request.form['host_fingerprint'].strip()
+		if not cur_host_fp:
+			cur_host_fp = None
+	except:
+		cur_host_fp = None
 
-def validateAddClusterInitial(self, request, must_complete=True):
-	errors = list()
-	messages = list()
-	newNodeList = list()
-	nodeHash = {}
-	rnodeHash = {}
+	try:
+		rc = RicciCommunicator(cur_host)
+		if not rc:
+			raise Exception, 'rc is None'
+		cur_fp = rc.fingerprint()
+		if cur_host_fp is not None:
+			cur_entry['fp'] = cur_host_fp
+		else:
+			cur_entry['fp'] = cur_fp[1]
+	except Exception, e:
+		luci_log.debug_verbose('vACI2: %s: %s' % (cur_host, str(e)))
+		request.SESSION.set('add_cluster_initial', cur_entry)
+		return (False, { 'errors': [ 'Unable to establish a secure connection to the ricci agent on %s: %s' \
+			% (cur_host, str(e)) ] })
+
+	if not check_certs or cur_host_trusted:
+		try:
+			if cur_host_fp is not None and cur_host_fp != cur_fp[1]:
+				errmsg = 'The key fingerprint for %s has changed from under us. It was \"%s\" and is now \"%s\".' \
+					% (cur_host, cur_host_fp, cur_fp[1])
+				request.SESSION.set('add_cluster_initial', cur_entry)
+				luci_log.info('SECURITY: %s' % errmsg)
+				return (False, { 'errors': [ errmsg ] })
+			rc.trust()
+		except Exception, e:
+			luci_log.debug_verbose('vACI3: %s %s' % (cur_host, str(e)))
+			request.SESSION.set('add_cluster_initial', cur_entry)
+			return (False, { 'errors': [ 'Unable to establish trust for host %s' % (cur_host, str(e)) ] })
+	elif check_certs:
+		if not rc.trusted():
+			msg = '%s has %s fingerprint %s' \
+				% (cur_host, cur_fp[0], cur_fp[1])
+		else:
+			cur_host_trusted = True
+			cur_entry['trusted'] = True
+			msg = 'Host %s %s fingerprint %s is already trusted.' \
+				% (cur_host, cur_fp[0], cur_fp[1])
+		request.SESSION.set('add_cluster_initial', cur_entry)
+		messages.append(msg)
+		return (True, { 'messages': [ msg ] })
 
 	try:
-		sysData = request.form['__SYSTEM0']
-		if not sysData or len(sysData) < 2:
-			raise
+		del rc
+		request.SESSION.delete('add_cluster_initial')
 	except:
-		return (False, { 'errors': [ 'At least one system and its root password must be given' ] })
+		pass
 
 	try:
-		rc = RicciCommunicator(sysData[0])
+		rc = RicciCommunicator(cur_host, enforce_trust=True)
 		if not rc:
-			raise Exception, 'unknown error'
+			raise Exception, 'rc is None'
+		cur_entry['trusted'] = rc.trusted()
 	except Exception, e:
-		return (False, { 'errors': [ 'Unable to establish a connection to the Ricci agent on %s: %s' % (sysData[0], str(e)) ] })
+		luci_log.debug_verbose('vACI4: %s %s' % (cur_host, str(e)))
+		request.SESSION.set('add_cluster_initial', cur_entry)
+		return (False, { 'errors': [ 'Unable to connect to the ricci agent on %s' % cur_host ] })
 
-	prevAuth = 0
-	if not rc.authed():
+	prev_auth = rc.authed()
+	if not prev_auth:
 		try:
-			rc.auth(sysData[1])
-		except: pass
-		if not rc.authed():
-			return (False, { 'errors': [ 'Unable to authenticate to the Ricci agent on \"' + sysData[0] + '\"' ] })
-	else:
-		prevAuth = 1
+			rc.auth(cur_pass)
+			if not rc.authed():
+				raise Exception, 'authentication failed'
+		except Exception, e:
+			errmsg = 'Unable to authenticate to the ricci agent on %s: %s' % (cur_host, str(e))
+			luci_log.debug_verbose('vACI5: %s: %s' % (cur_host, str(e)))
+			request.SESSION.set('add_cluster_initial', cur_entry)
+			return (False, { 'errors': [ 'Unable to authenticate to the ricci agent on \"%s\"' % cur_host ] })
+
+	del cur_entry
 
 	try:
 		cluster_info = rc.cluster_info()
 	except:
 		cluster_info = None
 
-	os_str = resolveOSType(rc.os())
-	if not os_str:
-		os_str = "rhel5"  #Backup plan in case all is almost lost...
-
 	if not cluster_info or not cluster_info[0]:
-		if not prevAuth:
-			rc.unauth()
+		if not prev_auth:
+			try:
+				rc.unauth()
+			except:
+				pass
+
 		if not cluster_info:
-			errmsg = 'An error occurred while attempting to retrieve the cluster.conf file for \"' + sysData[0] + '\"'
+			errmsg = 'An error occurred while attempting to retrieve the cluster.conf file from \"%s\"' % cur_host
 		else:
-			errmsg = '\"' + sysData[0] + '\" is not a member of a cluster'
+			errmsg = '\"%s\" reports is not a member of any cluster.'
 		return (False, { 'errors': [ errmsg ] })
 
-	clusterName = cluster_info[0]
-	cluConf = getClusterConf(rc)
-	if cluConf:
-		nodeList = getClusterConfNodes(cluConf)
-
-	if not cluConf or not nodeList or len(nodeList) < 1:
-		if not prevAuth:
-			rc.unauth()
-		return (False, { 'errors': [ 'Error retrieving member nodes for cluster \"' + clusterName + '\"' ] })
-
-	systemName = rc.system_name()
-	if systemName[:9] == 'localhost':
-		systemName = sysData[0]
-
-	node = { 'host': rc.hostname(), 'ricci_host': systemName, 'prev_auth': prevAuth, 'cur_auth': rc.authed(), 'os': os_str }
-	nodeHash[sysData[0]] = node
-	rnodeHash[systemName] = node
-	newNodeList.append(node)
+	cluster_name = cluster_info[0]
+	cluster_os = resolveOSType(rc.os())
+	try:
+		cluster_conf = getClusterConf(rc)
+	except:
+		cluster_conf = None
 
-	if 'allSameCheckBox' in request.form:
-		passwd = sysData[1]
-	else:
-		passwd = None
-		
-	for i in nodeList:
-		node = nodeAuth(clusterName, i, passwd)
-		if 'messages' in node:
-			messages.extend(node['messages'])
-		if node['host'] in nodeHash or node['ricci_host'] in rnodeHash:
-			continue
-		nodeHash[node['host']] = node
-		if 'ricci_host' in node:
-			rnodeHash[node['ricci_host']] = node
-
-		if 'errors' in node:
-			errors.append(node['errors'])
-			node['errors'] = True
-		newNodeList.append(node)
-
-	sfn = lambda x, y: \
-		x['cur_auth'] - y['cur_auth'] or (('errors' in y) - ('errors' in x)) 
-	newNodeList.sort(sfn)
+	if cluster_conf:
+		try:
+			node_list = getClusterConfNodes(cluster_conf)
+		except:
+			node_list = None
 
-	if must_complete == True:
-		dfn = lambda x: not 'cur_auth' in x or x['cur_auth'] != True
-	else:
-		dfn = lambda x: False
+	# Make sure a cluster with this name is not already managed before
+	# going any further.
+	try:
+		dummy = self.restrictedTraverse(CLUSTER_FOLDER_PATH + cluster_name)
+		if not dummy:
+			raise Exception, 'no existing cluster'
+		errors.append('A cluster named \"%s\" is already managed.')
+		if not prev_auth:
+			try:
+				rc.unauth()
+			except:
+				pass
+		return (False, { 'errors': errors })
+	except:
+		pass
 
-	cluster_properties = {
-		'clusterName': clusterName,
-		'nodeList': newNodeList,
-		'nodeHash': nodeHash,
-		'rnodeHash': rnodeHash,
-		'isComplete': len(filter(dfn, newNodeList)) == 0
-	}
+	if not cluster_conf or not node_list or len(node_list) < 1:
+		if not prev_auth:
+			try:
+				rc.unauth()
+			except:
+				pass
+		return (False, { 'errors': [ 'Error retrieving the nodes list for cluster \"%s\" from node \"%s\"' % (cluster_name, cur_host) ] })
 
-	if len(errors) < len(nodeList):
-		cluster_properties['redirect'] = HOMEBASE_ADD_CLUSTER
+	same_node_passwds = False
+	try:
+		same_node_passwds = 'allSameCheckBox' in request.form
+	except:
+		same_node_passwds = False
 
-	return (len(errors) < 1,
-		{'messages': messages, 'errors': errors, 'requestResults': cluster_properties })
+	add_cluster = { 'name': cluster_name,
+					'nodes': {},
+					'cluster_os':cluster_os,
+					'pass': 0,
+					'identical_passwds': same_node_passwds,
+					'check_certs': check_certs }
+
+	for i in node_list:
+		cur_node = { 'host': i }
+		if same_node_passwds:
+			cur_node['passwd'] = cur_pass
+		add_cluster['nodes'][i] = cur_node
+	request.SESSION.set('add_cluster', add_cluster)
+	request.response.redirect('/luci/homebase/index_html?pagetype=%s' % HOMEBASE_ADD_CLUSTER)
 
-def validateAddCluster(self, request, must_complete=True):
+def parseHostForm(request, check_certs):
 	errors = list()
 	messages = list()
-	requestResults = None
-	nodeList = None
+	system_list = {}
 
 	try:
-		sessionData = request.SESSION.get('checkRet')
-		requestResults = sessionData['requestResults']
+		num_storage = int(request.form['numStorage'].strip())
 	except Exception, e:
-		luci_log.debug_verbose('VAC0: error getting session obj: %s' % str(e))
+		luci_log.debug_verbose('PHF1: numStorage field missing: %s' % str(e))
+		errors.append('The number of systems entered could not be determined.')
+
+	incomplete = False
+	i = 0
+	while i < num_storage:
 		try:
-			clusterName = request.form['clusterName']
+			sysData = request.form['__SYSTEM%d' % i]
+			if len(sysData) < 1 or not sysData[0]:
+				raise Exception, 'no hostname'
+			cur_host = sysData[0]
+			if cur_host in system_list:
+				errors.append('You have added \"%s\" more than once.' % cur_host)
+				raise Exception, '%s added more than once' % cur_host
 		except:
-			clusterName = ''
+			i += 1
+			continue
+
+		cur_system = { 'host': cur_host }
+
+		if len(sysData) < 2 or not sysData[1]:
+			errors.append('No password for %s (entry %d).' % (cur_host, i))
+			cur_passwd = None
+		else:
+			cur_passwd = sysData[1]
+			cur_system['passwd'] = cur_passwd
 
 		try:
-			nodeList = requestResults['nodeList']
-			luci_log.debug_verbose('VAC1: unauth to node list')
-			nodeUnauth(nodeList)
+			cur_fp = request.form['__SYSTEM%dFingerprint' % i].strip()
+			if not cur_fp:
+				raise Exception, 'fingerprint is blank'
+			cur_system['fp'] = cur_fp
 		except:
-			pass
+			cur_fp = None
+
+		try:
+			cur_set_trust = request.form.has_key('__SYSTEM%dTrusted' % i)
+		except:
+			cur_set_trust = False
+
+		if check_certs or (cur_fp is not None and cur_set_trust is True):
+			try:
+				rc = RicciCommunicator(cur_host, enforce_trust=False)
+				if not rc:
+					raise Exception, 'rc is None'
+				cur_system['prev_auth'] = rc.authed()
+				fp = rc.fingerprint()
+
+				if cur_set_trust is True:
+					cur_system['fp'] = cur_fp
+					if cur_fp != fp[1]:
+						errmsg = 'The key fingerprint for %s has changed from under us. It was \"%s\" and is now \"%s\".' % (cur_host, cur_fp, fp[1])
+						errors.append(errmsg)
+						luci_log.info('SECURITY: %s' % errmsg)
+						cur_system['error'] = True
+						incomplete = True
+					else:
+						rc.trust()
+						cur_system['trusted'] = True
+				else:
+					cur_system['fp'] = fp[1]
+
+				if not rc.trusted():
+					incomplete = True
+					msg = '%s has %s fingerprint %s' % (cur_host, fp[0], fp[1])
+				else:
+					cur_system['trusted'] = True
+					msg = '%s %s fingerprint %s is already trusted.' % (cur_host, fp[0], fp[1])
+
+				if check_certs:
+					messages.append(msg)
+			except Exception, e:
+				cur_system['error'] = True
+				try:
+					del cur_system['trusted']
+				except:
+					pass
+				errors.append('Unable to retrieve the SSL fingerprint for node %s: %s' % (cur_host, str(e)))
+				luci_log.debug_verbose('PHF2: %s: %s' \
+					% (cur_host, str(e)))
+		else:
+			# The user doesn't care. Trust the system.
+			try:
+				rc = RicciCommunicator(cur_host)
+				if not rc:
+					raise Exception, 'rc is None'
+				rc.trust()
+				cur_system['trusted'] = True
+				cur_system['prev_auth'] = rc.authed()
+			except Exception, e:
+				incomplete = True
+				cur_system['error'] = True
+				try:
+					if not 'prev_auth' in cur_system:
+						del cur_system['trusted']
+						rc.untrust()
+				except:
+					pass		
+				errors.append('Unable to add the key for node %s to the trusted keys list.' % cur_host)
+				luci_log.debug_verbose('PHF3: %s: %s' % (cur_host, str(e)))
+		system_list[cur_host] = cur_system
+		i += 1
+
+	return system_list, incomplete, errors, messages
+
+def validateAddCluster(self, request):
+	errors = list()
 
-		return (False, { 'errors': [ 'A data integrity error has occurred. Please attempt adding the cluster again.' ], 'requestResults': { 'clusterName': clusterName, 'isComplete': False, 'nodeList': [], 'redirect': HOMEBASE_ADD_CLUSTER_INITIAL } })
-		
 	try:
-		clusterName = request.form['clusterName']
-		if not clusterName:
-			raise Exception, 'no cluster name was found'
-	except Exception, e:
-		luci_log.debug_verbose('VAC2: no cluser name found: %s', str(e))
-		return (False, { 'errors': ['No cluster name was given.'], 'requestResults': requestResults })
+		request.SESSION.delete('add_cluster')
+		request.SESSION.delete('add_cluster_initial')
+	except:
+		pass
 
 	try:
-		nodeList = requestResults['nodeList']
-		if not nodeList or len(nodeList) < 1:
-			raise Exception, 'no node list found'
-	except Exception, e:
-		luci_log.debug_verbose('VAC3: no nodeList found: %s', str(e))
-		return (False, { 'errors': ['No cluster nodes were given.'], 'requestResults': requestResults })
+		cluster_name = request.form['clusterName'].strip()
+	except:
+		luci_log.debug_verbose('VAC0: no cluster name')
+		errors.append('No cluster name was given.')
 
 	try:
-		nodeHash = requestResults['nodeHash']
+		cluster_os = request.form['cluster_os'].strip()
 	except:
-		nodeHash = {}
+		luci_log.debug_verbose('VAC1: no cluster os')
+		errors.append('Unable to determine the version of cluster %s.' % cluster_name)
 
+	check_certs = False
 	try:
-		rnodeHash = requestResults['rnodeHash']
+		check_certs = 'check_certs' in request.form
 	except:
-		rnodeHash = {}
+		check_certs = False
 
-	# This should never fail
 	try:
-		numStorage = int(request.form['numStorage'])
-		if numStorage != len(nodeList):
-			raise Exception, 'numstorage != len(nodelist)'
-	except Exception, e:
+		pass_num = int(request.form['pass'].strip()) + 1
+	except:
+		pass_num = 1
+
+	same_node_passwds = False
+	try:
+		same_node_passwds = 'allSameCheckBox' in request.form
+	except:
+		same_node_passwds = False
+
+	add_cluster = { 'name': cluster_name,
+					'pass': pass_num,
+					'cluster_os': cluster_os,
+					'identical_passwds': same_node_passwds,
+					'check_certs': check_certs }
+
+	system_list, incomplete, new_errors, messages = parseHostForm(request, check_certs)
+	errors.extend(new_errors)
+	add_cluster['nodes'] = system_list
+
+	for i in system_list:
+		cur_system = system_list[i]
+
+		cur_host_trusted = 'trusted' in cur_system
+		cur_host = cur_system['host']
+		prev_auth = False
 		try:
-			requestResults['isComplete'] = False
-			luci_log.debug_verbose('VAC4: error: %s' % str(e))
+			cur_passwd = cur_system['passwd']
 		except:
-			pass
+			cur_passwd = None
 
-		nodeUnauth(nodeList)
-		return (False, {
-				'errors': [ 'Unknown number of nodes entered' ],
-				'requestResults': requestResults })
+		if (cur_host_trusted or not check_certs) and cur_passwd:
+			try:
+				rc = RicciCommunicator(cur_host, enforce_trust=False)
+				prev_auth = rc.authed()
+			except Exception, e:
+				errors.append('Unable to connect to the ricci agent on %s: %s' \
+					% (cur_host, str(e)))
+				incomplete = True
+				cur_system['errors'] = True
+				luci_log.debug_verbose('VAC2: %s: %s' % cur_host, str(e))
+				continue
 
-	i = 0
-	while i < numStorage:
-		sysData = request.form['__SYSTEM' + str(i)]
-		if not sysData:
-			i += 1
-			continue
+			try:
+				rc.auth(cur_passwd)
+				if not rc.authed():
+					raise Exception, 'authentication failed'
+			except Exception, e:
+				errors.append('Unable to authenticate to the ricci agent on %s: %s' \
+					% (cur_host, str(e)))
+				incomplete = True
+				cur_system['errors'] = True
+				luci_log.debug_verbose('VAC3: %s: %s' % cur_host, str(e))
+				continue
 
-		oldNode = None
-		node = nodeAuth(clusterName, sysData[0], sysData[1])
-		if node['host'] in nodeHash:
-			oldNode = nodeHash[node['host']]
-		elif 'ricci_host' in node and node['ricci_host'] in rnodeHash:
-			oldNode = rnodeHash[node['ricci_host']]
-		elif not oldNode:
-			for k in nodeHash.keys():
-				if node['host'][:len(k) + 1] == k + '.':
-					oldNode = nodeHash[k]
-		elif not oldNode:
-			for k in rnodeHash.keys():
-				if node['host'][:len(k) + 1] == k + '.':
-					oldNode = rnodeHash[k]
-
-		if not oldNode:
-			luci_log.debug_verbose('VAC5: node %s not found', sysData[0])
-			nodeUnauth(nodeList)
-			return (False, { 'errors': [ 'A data integrity error has occurred. Please attempt adding the cluster again.' ], 'requestResults': { 'clusterName': clusterName, 'nodeList': nodeList, 'isComplete': False, 'redirect': HOMEBASE_ADD_CLUSTER_INITIAL } })
-
-		if oldNode['host'] != node['host']:
-			del nodeHash[oldNode['host']]
-			oldNode['host'] = node['host']
-			nodeHash[node['host']] = oldNode
-
-		if 'ricci_host' in node and (not 'ricci_host' in oldNode or node['ricci_host'] != oldNode['ricci_host']):
-			if oldNode['ricci_host'] in rnodeHash:
-				del rnodeHash[oldNode['ricci_host']]
-				oldNode['ricci_host'] = node['ricci_host']
-				rnodeHash[node['ricci_host']] = oldNode
-
-		oldNode['cur_auth'] = node['cur_auth']
-		if 'errors' in node:
-			errors.append(node['errors'])
-			oldNode['errors'] = True
-		i += 1
+			cluster_info = rc.cluster_info()
+			if cluster_info[0] != cluster_name and cluster_info[1] != cluster_name:
+				incomplete = True
+				cur_system['errors'] = True
+
+				if cluster_info[0]:
+					cur_cluster_name = cluster_info[0]
+				else:
+					cur_cluster_name = cluster_info[1]
+
+				if cur_cluster_name:
+					err_msg = 'Node %s reports it is in cluster \"%s\" and we expect \"%s\"' \
+						% (cur_host, cur_cluster_name % cluster_name)
+				else:
+					err_msg = 'Node %s reports it is not a member of any cluster' % cur_host
+
+				if not prev_auth:
+					try:
+						rc.unauth()
+					except:
+						luci_log.debug_verbose('VAC4: %s: %s' % (cur_host, str(e)))
 
-	if must_complete == True:
-		dfn = lambda x: not 'cur_auth' in x or x['cur_auth'] != True
-	else:
-		dfn = lambda x: False
+				errors.append(err_msg)
+				luci_log.debug_verbose('VAC5: %s' % err_msg)
+				continue
 
-	clusterComplete = len(filter(dfn, nodeList)) == 0
+			cur_os = resolveOSType(rc.os())
+			if cur_os != cluster_os:
+				incomplete = True
+				cur_system['errors'] = True
+
+				if not prev_auth:
+					try:
+						rc.unauth()
+					except Exception, e:
+						luci_log.debug_verbose('VAC6: %s: %s' % (cur_host, str(e)))
 
-	if clusterComplete:
-		err = manageCluster(self, clusterName, nodeList)
-		if err:
-			errors.append(err)
+				err_msg = 'Node %s reports its cluster version is %s and we expect %s' \
+					% (cur_os, cluster_os)
+
+				errors.append(err_msg)
+				luci_log.debug_verbose('VAC7: %s' % err_msg)
+				continue
 		else:
-			messages.append('Cluster \"' + clusterName + '\" has been added to the Luci management interface.') 
-	else:
-		sfn = lambda x, y: \
-			x['cur_auth'] - y['cur_auth'] or (('errors' in y) - ('errors' in x)) 
-		nodeList.sort(sfn)
-
-	ret = { 'messages': messages, 'errors': errors }
-
-	if len(errors) > 0 or not clusterComplete:
-		ret['requestResults'] = {
-			'clusterName': clusterName,
-			'nodeList': nodeList,
-			'nodeHash': nodeHash,
-			'rnodeHash': rnodeHash,
-			'isComplete': clusterComplete
-		}
-	else:
-		ret['requestResults'] = {
-			'redirect': HOMEBASE_ADD_CLUSTER_INITIAL,
-			'clusterName': clusterName,
-			'isComplete': True
-		}
+			incomplete = True
 
-	return (len(errors) < 1, ret)
+	if len(errors) > 0:
+		incomplete = True
+
+	if not incomplete or request.form.has_key('asis'):
+		err_msg = manageCluster(self, cluster_name, system_list, cluster_os)
+		if err_msg:
+			incomplete = True
+			errors.append('An error occurred while creating the database objects for cluster %s: %s' \
+				% (cluster_name, err_msg))
+			luci_log.debug_verbose('VAC7: error adding cluster DB objects for %s: %s' \
+				% (cluster_name, err_msg))
+		else:
+			messages.append('Cluster %s is now managed by Luci' % cluster_name)
+			incomplete = False
+
+	if incomplete:
+		add_cluster['incomplete'] = True
+		request.SESSION.set('add_cluster', add_cluster)
+		return_code = False
+	else:
+		return_code = True
+	
+	return (return_code, {'errors': errors, 'messages': messages })
 
 def validateAddSystem(self, request):
-	errors = list()
-	messages = list()
+	try:
+		request.SESSION.delete('add_systems')
+	except:
+		pass
 
+	check_certs = False
 	try:
-		numStorage = request.form['numStorage']
+		check_certs = 'check_certs' in request.form
 	except:
-		return (False, { 'errors': ['Unknown number of systems entered'] })
+		check_certs = False
+
+	add_systems, incomplete, errors, messages = parseHostForm(request, check_certs)
+	delete_keys = list()
+	for i in add_systems:
+		cur_system = add_systems[i]
+
+		cur_host_trusted = 'trusted' in cur_system
+		cur_host = cur_system['host']
 
-	i = 0
-	while i < numStorage:
 		try:
-			sysData = request.form['__SYSTEM' + str(i)]
+			cur_passwd = cur_system['passwd']
 		except:
-			break
-
-		if len(sysData) == 2 and sysData[0] != '' and sysData[1] != '':
-			csResult = createSystem(self, sysData[0], sysData[1])
+			cur_passwd = None
 
+		if (cur_host_trusted or not check_certs) and cur_passwd:
+			csResult = createSystem(self, cur_host, cur_passwd)
 			if csResult:
+				incomplete = True
+				cur_system['error'] = True
 				errors.append(csResult)
 			else:
-				messages.append('Added storage system \"' + sysData[0] + '\" successfully')
-		i += 1
+				delete_keys.append(i)
+				messages.append('Added storage system \"%s\" successfully' \
+					% cur_host)
+
+	for i in delete_keys:
+		try:
+			del add_systems[i]
+		except:
+			pass
 
 	if len(errors) > 0:
-		returnCode = False
+		return_code = False
 	else:
-		returnCode = True
+		return_code = True
+
+	if incomplete:
+		try:
+			request.SESSION.set('add_systems', add_systems)
+		except Exception, e:
+			luci_log.debug_verbose('validateSA2: %s' % str(e))
+		return_code = False
+	else:
+		try:
+			request.SESSION.delete('add_systems')
+			del add_systems
+		except:
+			pass
 
-	return (returnCode, {'errors': errors, 'messages': messages})
+	return (return_code, { 'errors': errors, 'messages': messages})
 
 def validatePerms(self, request):
 	userId = None
@@ -599,62 +763,99 @@
 	return (returnCode, {'errors': errors, 'messages': messages, 'params': {'user': userId }})
 
 def validateAuthenticate(self, request):
-	errors = list()
-	messages = list()
+	try:
+		request.SESSION.delete('auth_systems')
+	except:
+		pass
 
+	check_certs = False
 	try:
-		numStorage = int(request.form['numStorage'])
+		check_certs = 'check_certs' in request.form
 	except:
-		return (False, {'errors': [ 'Unknown number of nodes entered']})
+		check_certs = False
 
-	i = 0
-	while i < numStorage:
-		sysData = request.form['__SYSTEM' + str(i)]
-		if not sysData or len(sysData) < 2 or not sysData[0] or not sysData[1]:
-			i += 1
-			continue
+	system_list, incomplete, errors, messages = parseHostForm(request, check_certs)
+	delete_keys = list()
+	for i in system_list:
+		cur_system = system_list[i]
 
-		host = str(sysData[0])
-		passwd = str(sysData[1])
+		cur_host_trusted = 'trusted' in cur_system
+		cur_host = cur_system['host']
 
 		try:
-			rc = RicciCommunicator(sysData[0])
-			if rc is None:
-				raise Exception, 'unknown error'
-		except Exception, e:
-			errors.append('Unable to contact the ricci agent for %s: %s' \
-				% (sysData[0], str(e)))
-			i += 1
-			continue
+			cur_passwd = cur_system['passwd']
+		except:
+			cur_passwd = None
 
-		if rc.authed():
-			messages.append(host + ' is already authenticated.')
-		else:
+		if (cur_host_trusted or not check_certs) and cur_passwd:
 			try:
-				rc.auth(passwd)
-			except:
-				errors.append('Error authenticating to the ricci agent on ' + host)
-				i += 1
+				rc = RicciCommunicator(cur_host, enforce_trust=True)
+				if not rc:
+					raise Exception, 'connection failed'
+			except Exception, e:
+				luci_log.debug_verbose('validateAuth0: %s: %s' % (cur_host, str(e)))
+				errors.append('Unable to communicate with the ricci agent on %s: %s' \
+					% (cur_host, str(e)))
+				incomplete = True
+				cur_system['error'] = True
 				continue
 
-			if not rc.authed():
-				errors.append('Error authenticating to the ricci agent on ' + host)
-			else:
-				messages.append(host + ' was successfully authenticated.')
-
-			if rc.authed():
+			try:
+				if rc.authed():
+					messages.append('%s is already authenticated.' % cur_host)
+				else:
+					rc.auth(cur_passwd)
+					if not rc.authed():
+						raise Exception, 'authentication failed'
+					messages.append('Authenticated to %s successfully' \
+						% cur_host)
+				delete_keys.append(i)
 				try:
-					delNodeFlag(self, getStorageNode(self, host), CLUSTER_NODE_NEED_AUTH)
+					delNodeFlag(self, getStorageNode(self, cur_host), CLUSTER_NODE_NEED_AUTH)
 				except:
 					pass
 
 				try:
-					delNodeFlag(self, getClusterNode(self, host, rc.cluster_info()[0]), CLUSTER_NODE_NEED_AUTH)
+					delNodeFlag(self, getClusterNode(self, cur_host, rc.cluster_info()[0]), CLUSTER_NODE_NEED_AUTH)
 				except:
 					pass
-		i += 1
-			
-	return (len(errors) > 0, {'errors': errors, 'messages': messages })
+			except Exception, e:
+				errors.append('Unable to authenticate to %s: %s' % (cur_host, str(e)))
+				luci_log.debug_verbose('validateAuth1: %s: %s' % (cur_host, str(e)))
+				incomplete = True
+				cur_system['error'] = True
+
+	for i in delete_keys:
+		try:
+			del system_list[i]
+		except:
+			pass
+
+	if len(errors) > 0:
+		return_code = False
+	else:
+		return_code = True
+
+	if incomplete:
+		try:
+			request.SESSION.set('auth_systems', system_list)
+		except Exception, e:
+			luci_log.debug_verbose('validateAuthenticate2: %s' % str(e))
+		return_code = False
+	else:
+		try:
+			request.SESSION.delete('auth_systems')
+			del auth_systems
+		except:
+			pass
+
+	auth_msgs = {}
+	if len(errors) > 0:
+		auth_msgs['errors'] = errors
+	if len(messages) > 0:
+		auth_msgs['messages'] = messages
+	request.SESSION.set('auth_status', auth_msgs)
+	request.response.redirect('/luci/homebase/index_html?pagetype=5')
 
 formValidators = [
 	validateAddUser,
@@ -690,11 +891,6 @@
 	return False
 
 def homebaseControlPost(self, request):
-	try:
-		sessionData = request.SESSION.get('checkRet')
-	except:
-		sessionData = None
-
 	if 'ACTUAL_URL' in request:
 		url = request['ACTUAL_URL']
 	else:
@@ -718,24 +914,14 @@
 			pass
 		return homebasePortal(self, request, '.', '0')
 
-	if validatorFn == validateAddClusterInitial or validatorFn == validateAddCluster:
-		ret = validatorFn(self, request, must_complete=False)
-	else:
-		ret = validatorFn(self, request)
+	ret = validatorFn(self, request)
 	params = None
 
-	if 'params' in ret[1]:
-		params = ret[1]['params']
-
-	if 'requestResults' in ret[1]:
-		requestResults = ret[1]['requestResults']
+	if ret and len(ret) > 1 and ret[1]:
+		if 'params' in ret[1]:
+			params = ret[1]['params']
+		request.SESSION.set('checkRet', ret[1])
 
-		if 'redirect' in requestResults:
-			pagetype = requestResults['redirect']
-			request['pagetype'] = pagetype
-			request.form['pagetype'] = pagetype
-
-	request.SESSION.set('checkRet', ret[1])
 	return homebasePortal(self, request, url, pagetype, params)
 
 def homebaseControl(self, request):
@@ -789,33 +975,12 @@
 
 	# Initial add cluster page
 	try:
-		if pagetype == HOMEBASE_ADD_CLUSTER:
-			raise
 		if havePermAddCluster(self):
 			addCluster = {}
 			addCluster['Title'] = 'Add an Existing Cluster'
 			addCluster['absolute_url'] = url + '?pagetype=' + HOMEBASE_ADD_CLUSTER_INITIAL
 			addCluster['Description'] = 'Add an existing cluster to the Luci cluster management interface.'
-			if pagetype == HOMEBASE_ADD_CLUSTER_INITIAL:
-				addCluster['currentItem'] = True
-				ret['curIndex'] = index
-				cur = addCluster
-			else:
-				addCluster['currentItem'] = False
-			index += 1
-			temp.append(addCluster)
-	except: pass
-
-	# Add cluster - screen 2
-	try:
-		if pagetype != HOMEBASE_ADD_CLUSTER:
-			raise
-		if havePermAddCluster(self):
-			addCluster = {}
-			addCluster['Title'] = 'Add an Existing Cluster'
-			addCluster['absolute_url'] = url + '?pagetype=' + HOMEBASE_ADD_CLUSTER
-			addCluster['Description'] = 'Add an existing cluster to the Luci cluster management interface.'
-			if pagetype == HOMEBASE_ADD_CLUSTER:
+			if pagetype == HOMEBASE_ADD_CLUSTER_INITIAL or pagetype == HOMEBASE_ADD_CLUSTER:
 				addCluster['currentItem'] = True
 				ret['curIndex'] = index
 				cur = addCluster
@@ -923,7 +1088,7 @@
 def getClusterSystems(self, clusterName):
 	if isAdmin(self):
 		try:
-			return self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName + '/objectItems')('Folder')
+			return self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName + '/objectItems')('Folder')
 		except Exception, e:
 			luci_log.debug_verbose('GCSy0: %s: %s' % (clusterName, str(e)))
 			return None
@@ -937,7 +1102,7 @@
 		return None
 
 	try:
-		csystems = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName + '/objectItems')('Folder')
+		csystems = self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName + '/objectItems')('Folder')
 		if not csystems or len(csystems) < 1:
 			return None
 	except Exception, e:
@@ -1024,20 +1189,19 @@
 
 def createSystem(self, host, passwd):
 	try:
-		exists = self.restrictedTraverse(PLONE_ROOT +'/systems/storage/' + host)
+		dummy = self.restrictedTraverse(STORAGE_FOLDER_PATH + host)
 		luci_log.debug_verbose('CS0: %s already exists' % host)
 		return 'Storage system %s is already managed' % host
 	except:
 		pass
 
 	try:
-		rc = RicciCommunicator(host)
+		rc = RicciCommunicator(host, enforce_trust=True)
 		if rc is None:
-			raise Exception, 'unknown error'
+			raise Exception, 'rc is None'
 	except Exception, e:
 		luci_log.debug_verbose('CS1: %s: %s' % (host, str(e)))
-		return 'Unable to establish a connection to the ricci agent on %s: %s' \
-			% (host, str(e))
+		return 'Unable to establish a secure connection to the ricci agent on %s: %s' % (host, str(e))
 
 	try:
 		if not rc.authed():
@@ -1056,21 +1220,21 @@
 		return 'Authentication for storage system %s failed' % host
 
 	try:
-		exists = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + host)
+		dummy = self.restrictedTraverse(STORAGE_FOLDER_PATH + host)
 		luci_log.debug_verbose('CS4 %s already exists' % host)
 		return 'Storage system %s is already managed' % host
 	except:
 		pass
 
 	try:
-		ssystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/')
+		ssystem = self.restrictedTraverse(STORAGE_FOLDER_PATH)
 	except Exception, e:
 		luci_log.debug_verbose('CS5 %s: %s' % (host, str(e)))
 		return 'Unable to create storage system %s: %s' % host
 
 	try:
 		ssystem.manage_addFolder(host, '__luci__:system')
-		newSystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + host)
+		newSystem = self.restrictedTraverse(STORAGE_FOLDER_PATH + host)
 	except Exception, e:
 		luci_log.debug_verbose('CS6 %s: %s' % (host, str(e)))
 		return 'Unable to create DB entry for storage system %s' % host
@@ -1085,28 +1249,22 @@
 	return None
 
 def abortManageCluster(self, request):
-	try:
-		sessionData = request.SESSION.get('checkRet')
-		nodeUnauth(sessionData['requestResults']['nodeList'])
-	except Exception, e:
-		luci_log.debug_verbose('AMC0: %s' % str(e))
+	pass
 
-def manageCluster(self, clusterName, nodeList):
+def manageCluster(self, clusterName, node_list, cluster_os):
 	clusterName = str(clusterName)
 
 	try:
-		clusters = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/')
+		clusters = self.restrictedTraverse(CLUSTER_FOLDER_PATH)
 		if not clusters:
 			raise Exception, 'cannot find the cluster entry in the DB'
 	except Exception, e:
-		nodeUnauth(nodeList)
 		luci_log.debug_verbose('MC0: %s: %s' % (clusterName, str(e)))
 		return 'Unable to create cluster %s: the cluster directory is missing.' % clusterName
 
 	try:
-		newCluster = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName)
+		newCluster = self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName)
 		if newCluster:
-			nodeUnauth(nodeList)
 			luci_log.debug_verbose('MC1: cluster %s: already exists' % clusterName)
 			return 'A cluster named %s is already managed by Luci' % clusterName
 	except:
@@ -1114,11 +1272,10 @@
 
 	try:
 		clusters.manage_addFolder(clusterName, '__luci__:cluster')
-		newCluster = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName)
+		newCluster = self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName)
 		if not newCluster:
 			raise Exception, 'unable to create the cluster DB entry for %s' % clusterName
 	except Exception, e:
-		nodeUnauth(nodeList)
 		luci_log.debug_verbose('MC2: %s: %s' % (clusterName, str(e)))
 		return 'Unable to create cluster %s: %s' % (clusterName, str(e))
 
@@ -1127,7 +1284,6 @@
 		newCluster.manage_role('View', ['Access Contents Information', 'View'])
 	except Exception, e:
 		luci_log.debug_verbose('MC3: %s: %s' % (clusterName, str(e)))
-		nodeUnauth(nodeList)
 		try:
 			clusters.manage_delObjects([clusterName])
 		except Exception, e:
@@ -1135,34 +1291,22 @@
 		return 'Unable to set permissions on new cluster: %s: %s' % (clusterName, str(e))
 
 	try:
-		cluster_os = nodeList[0]['os']
-		if not cluster_os:
-			raise KeyError, 'Cluster OS is blank'
-	except KeyError, e:
-		luci_log.debug_verbose('MC5: %s: %s' % (clusterName, str(e)))
-		cluster_os = 'rhel5'
-
-	try:
 		newCluster.manage_addProperty('cluster_os', cluster_os, 'string')
 	except Exception, e:
 		luci_log.debug_verbose('MC5: %s: %s: %s' \
 			% (clusterName, cluster_os, str(e)))
 
-	for i in nodeList:
-		#if 'ricci_host' in i:
-		#	host = str(i['ricci_host'])
-		#else:
-		host = str(i['host'])
+	for i in node_list:
+		host = node_list[i]['host']
 
 		try:
 			newCluster.manage_addFolder(host, '__luci__:csystem:' + clusterName)
-			newSystem = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName + '/' + host)
+			newSystem = self.restrictedTraverse(str(CLUSTER_FOLDER_PATH + clusterName + '/' + host))
 			if not newSystem:
 				raise Exception, 'unable to create cluster system DB entry for node %s' % host
 			newSystem.manage_acquiredPermissions([])
 			newSystem.manage_role('View', [ 'Access contents information' , 'View' ])
 		except Exception, e:
-			nodeUnauth(nodeList)
 			try:
 				clusters.manage_delObjects([clusterName])
 			except Exception, e:
@@ -1175,7 +1319,7 @@
 				% (host, clusterName, str(e))
 
 	try:
-		ssystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/')
+		ssystem = self.restrictedTraverse(STORAGE_FOLDER_PATH)
 		if not ssystem:
 			raise Exception, 'The storage DB entry is missing'
 	except Exception, e:
@@ -1184,83 +1328,78 @@
 
 	# Only add storage systems if the cluster and cluster node DB
 	# objects were added successfully.
-	for i in nodeList:
-		#if 'ricci_host' in i:
-		#	host = str(i['ricci_host'])
-		#else:
-		host = str(i['host'])
+	for i in node_list:
+		host = node_list[i]['host']
 
 		try:
 			# It's already there, as a storage system, no problem.
-			exists = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + host)
+			dummy = self.restrictedTraverse(str(STORAGE_FOLDER_PATH + host))
 			continue
 		except:
 			pass
 
 		try:
 			ssystem.manage_addFolder(host, '__luci__:system')
-			newSystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + host)
+			newSystem = self.restrictedTraverse(STORAGE_FOLDER_PATH + host)
 			newSystem.manage_acquiredPermissions([])
 			newSystem.manage_role('View', [ 'Access contents information' , 'View' ])
 		except Exception, e:
 			luci_log.debug_verbose('MC9: %s: %s: %s' % (clusterName, host, str(e)))
 
-def createClusterSystems(self, clusterName, nodeList):
+def createClusterSystems(self, clusterName, node_list):
 	try:
-		clusterObj = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName)
+		clusterObj = self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName)
 		if not clusterObj:
 			raise Exception, 'cluster %s DB entry is missing' % clusterName
 	except Exception, e:
-		nodeUnauth(nodeList)
 		luci_log.debug_verbose('CCS0: %s: %s' % (clusterName, str(e)))
-		return 'No cluster named \"' + clusterName + '\" is managed by Luci'
+		return 'No cluster named \"%s\" is managed by Luci' % clusterName
 
-	for i in nodeList:
-		#if 'ricci_host' in i:
-		#	host = str(i['ricci_host'])
-		#else:
+	for x in node_list:
+		i = node_list[x]
 		host = str(i['host'])
 
 		try:
 			clusterObj.manage_addFolder(host, '__luci__:csystem:' + clusterName)
-			newSystem = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName + '/' + host)
+		except Exception, e:
+			luci_log.debug_verbose('CCS0a: %s: %s: %s' % (clusterName, host, str(e)))
+		try:
+			newSystem = self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName + '/' + host)
 			if not newSystem:
 				raise Exception, 'cluster node DB entry for %s disappeared from under us' % host
 					
 			newSystem.manage_acquiredPermissions([])
 			newSystem.manage_role('View', [ 'Access contents information' , 'View' ])
 		except Exception, e:
-			nodeUnauth(nodeList)
 			luci_log.debug_verbose('CCS1: %s: %s: %s' % (clusterName, host, str(e)))
 			return 'Unable to create cluster node %s for cluster %s: %s' \
 				% (host, clusterName, str(e))
 
 	try:
-		ssystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/')
+		ssystem = self.restrictedTraverse(STORAGE_FOLDER_PATH)
 		if not ssystem:
 			raise Exception, 'storage DB entry is missing'
 	except Exception, e:
+		# This shouldn't fail, but if it does, it's harmless right now
 		luci_log.debug_verbose('CCS2: %s: %s' % (clusterName, host, str(e)))
-		return
+		return None
 
 	# Only add storage systems if the and cluster node DB
 	# objects were added successfully.
-	for i in nodeList:
-		#if 'ricci_host' in i:
-		#	host = str(i['ricci_host'])
-		#else:
+	for x in node_list:
+		i = node_list[x]
 		host = str(i['host'])
 
 		try:
 			# It's already there, as a storage system, no problem.
-			exists = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + host)
+			dummy = self.restrictedTraverse(STORAGE_FOLDER_PATH + host)
 			continue
 		except:
 			pass
 
 		try:
 			ssystem.manage_addFolder(host, '__luci__:system')
-			newSystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + host)
+			newSystem = self.restrictedTraverse(STORAGE_FOLDER_PATH + host)
 			newSystem.manage_acquiredPermissions([])
 			newSystem.manage_role('View', [ 'Access contents information' , 'View' ])
 		except Exception, e:
@@ -1268,7 +1407,7 @@
 
 def delSystem(self, systemName):
 	try:
-		ssystem = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/')
+		ssystem = self.restrictedTraverse(STORAGE_FOLDER_PATH)
 		if not ssystem:
 			raise Exception, 'storage DB entry is missing'
 	except Exception, e:
@@ -1298,7 +1437,7 @@
 			pass
 	else:
 		try:
-			newSystem = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + cluster_info[0] + '/' + systemName)
+			dummy = self.restrictedTraverse(CLUSTER_FOLDER_PATH + cluster_info[0] + '/' + systemName)
 		except:
 			try:
 				rc.unauth()
@@ -1314,7 +1453,7 @@
 
 def delCluster(self, clusterName):
 	try:
-		clusters = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/')
+		clusters = self.restrictedTraverse(CLUSTER_FOLDER_PATH)
 		if not clusters:
 			raise Exception, 'clusters DB entry is missing'
 	except Exception, e:
@@ -1333,7 +1472,7 @@
 
 def delClusterSystem(self, cluster, systemName):
 	try:
-		if not self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + systemName):
+		if not self.restrictedTraverse(STORAGE_FOLDER_PATH + systemName):
 			raise
 	except:
 		# It's not a storage system, so unauthenticate.
@@ -1353,7 +1492,7 @@
 
 def delClusterSystems(self, clusterName):
 	try:
-		cluster = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + clusterName)
+		cluster = self.restrictedTraverse(CLUSTER_FOLDER_PATH + clusterName)
 		if not cluster:
 			raise Exception, 'cluster DB entry is missing'
 
@@ -1510,7 +1649,7 @@
 
 def getClusterNode(self, nodename, clustername):
 	try:
-		cluster_node = self.restrictedTraverse(PLONE_ROOT + '/systems/cluster/' + str(clustername) + '/' + str(nodename))
+		cluster_node = self.restrictedTraverse(CLUSTER_FOLDER_PATH + str(clustername) + '/' + str(nodename))
 		if not cluster_node:
 			raise Exception, 'cluster node is none'
 		return cluster_node
@@ -1521,7 +1660,7 @@
 
 def getStorageNode(self, nodename):
 	try:
-		storage_node = self.restrictedTraverse(PLONE_ROOT + '/systems/storage/' + '/' + str(nodename))
+		storage_node = self.restrictedTraverse(STORAGE_FOLDER_PATH + str(nodename))
 		if not storage_node:
 			raise Exception, 'storage node is none'
 		return storage_node
--- conga/luci/site/luci/Extensions/ricci_bridge.py	2006/12/12 19:03:06	1.51
+++ conga/luci/site/luci/Extensions/ricci_bridge.py	2006/12/21 05:08:49	1.52
@@ -597,6 +597,8 @@
 	# temporary workaround for ricci bug
 	system_info = rc.hostname()
 	try:
+#		FIXME
+#		rc = RicciCommunicator(system_info, enforce_trust=True)
 		rc = RicciCommunicator(system_info)
 		if rc is None:
 			raise Exception, 'unknown error'
--- conga/luci/site/luci/Extensions/ricci_communicator.py	2006/12/06 22:34:09	1.22
+++ conga/luci/site/luci/Extensions/ricci_communicator.py	2006/12/21 05:08:49	1.23
@@ -83,7 +83,19 @@
         luci_log.debug_verbose('RC:dom0: [auth %d] reported system_name = %s for %s' \
             % (self.__authed, self.__dom0, self.__hostname))
         return self.__dom0
-    
+
+    def fingerprint(self):
+		return self.ss.peer_fingerprint()
+
+    def trust(self):
+        return self.ss.trust()
+
+    def untrust(self):
+        return self.ss.untrust()
+
+    def trusted(self):
+        return self.ss.trusted()
+
     def auth(self, password):
         if self.authed():
             luci_log.debug_verbose('RC:auth0: already authenticated to %s' \
@@ -126,6 +138,10 @@
                 % (ret, self.__hostname))
             if ret != '0':
                 raise Exception, 'Invalid response'
+            try:
+                self.ss.untrust()
+            except:
+                pass
         except:
             errstr = 'Error authenticating to host %s: %s' \
                         % (self.__hostname, str(ret))




More information about the Cluster-devel mailing list