[luci] Fix fence brokenness exposed by the stricter xml parser
by Ryan McCabe
commit 389271cf753fc863d75d042fd3208a6b0e2f463d
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Mon Feb 21 22:31:39 2011 -0500
Fix fence brokenness exposed by the stricter xml parser
luci/controllers/cluster.py | 124 +++++++++++++++++++++++++----------------
luci/lib/ClusterConf/Fence.py | 15 +++++
luci/public/js/node.js | 33 +++++++++++
luci/templates/node.html | 34 ++++++------
4 files changed, 140 insertions(+), 66 deletions(-)
---
diff --git a/luci/controllers/cluster.py b/luci/controllers/cluster.py
index 524ceed..4fb9c28 100644
--- a/luci/controllers/cluster.py
+++ b/luci/controllers/cluster.py
@@ -167,73 +167,89 @@ class IndividualClusterController(BaseController):
flash(_('Unable to contact any nodes in this cluster'),
status="error")
redirect(tmpl_context.cluster_url)
+ cur_nodename = kw.get('node')
if kw.get("method_to_remove"):
- node = self.model.retrieveNodeByName(kw.get("node"))
+ node = self.model.retrieveNodeByName(cur_nodename)
methodname = kw.get("method_to_remove")
+ found_method = False
for child in node.getFenceNode().getChildren():
if child.getName() == methodname:
for child_device in child.getChildren():
node.removeFenceInstance(child, child_device)
+ found_method = True
node.getFenceNode().removeChild(child)
break
- self.model.setModified(True)
- rh.update_cluster_conf(self.model)
+ if found_method is True:
+ log.info('User "%s" removed method "%s" from node "%s" in cluster "%s"'
+ % (self.username, methodname, cur_nodename, self.name))
+ self.model.setModified(True)
+ rh.update_cluster_conf(self.model)
+ else:
+ log.error('User "%s" failed to remove method "%s" from node "%s" in cluster "%s"'
+ % (self.username, methodname, cur_nodename, self.name))
- if kw.get("fenceinst_to_remove") and kw.get("fenceinst_to_remove_method"):
- node = self.model.retrieveNodeByName(kw.get("node"))
+ elif kw.get("fenceinst_to_remove") and kw.get("fenceinst_to_remove_method"):
+ found_instance = False
+ node = self.model.retrieveNodeByName(cur_nodename)
fenceinst = kw.get("fenceinst_to_remove")
methodname = kw.get("fenceinst_to_remove_method")
for child in node.getFenceNode().getChildren():
if child.getName() == methodname:
- node.removeFenceInstance(child, child.children[int(fenceinst)-1])
- self.model.setModified(True)
- rh.update_cluster_conf(self.model)
+ node.removeFenceInstance(child, child.children[int(fenceinst) - 1])
+ found_instance = True
+ if found_instance is True:
+ log.info('User "%s" removed fence instance "%s" from method "%s" from node "%s" in cluster "%s"'
+ % (self.username, fenceinst, methodname, cur_nodename, self.name))
+ self.model.setModified(True)
+ rh.update_cluster_conf(self.model)
+ else:
+ log.error('User "%s" failed to remove fence instance "%s" from method "%s" from node "%s" in cluster "%s"'
+ % (self.username, fenceinst, methodname, cur_nodename, self.name))
- if kw.get("moveup_method"):
- node = self.model.retrieveNodeByName(kw.get("node"))
+ elif kw.get("moveup_method"):
+ node = self.model.retrieveNodeByName(cur_nodename)
methodname = kw.get("moveup_method")
- levels = node.getFenceMethods()
- for level in levels:
- if level.getAttribute("name") == methodname:
- position = levels.index(level)
- if position > 0:
- levels.pop(position)
- levels.insert(position-1,level)
- self.model.setModified(True)
- rh.update_cluster_conf(self.model)
- break
-
- if kw.get("movedown_method"):
- node = self.model.retrieveNodeByName(kw.get("node"))
+ fence_elem = node.getFenceNode()
+ if fence_elem is not None:
+ mposition = fence_elem.findMethod(methodname)
+ if mposition is not None and mposition > 0:
+ fence_elem.moveMethodUp(mposition)
+ self.model.setModified(True)
+ rh.update_cluster_conf(self.model)
+ log.info('User "%s" moved up fence method "%s" for node "%s" in cluster "%s"'
+ % (self.username, methodname, cur_nodename, self.name))
+
+ elif kw.get("movedown_method"):
+ node = self.model.retrieveNodeByName(cur_nodename)
methodname = kw.get("movedown_method")
+ fence_elem = node.getFenceNode()
levels = node.getFenceMethods()
- for level in levels:
- if level.getAttribute("name") == methodname:
- position = levels.index(level)
- if position < len(levels) - 1:
- levels.pop(position)
- levels.insert(position+1,level)
- self.model.setModified(True)
- rh.update_cluster_conf(self.model)
- break
-
- if command == "AddFence":
- node = self.model.retrieveNodeByName(kw.get("node"))
+ if fence_elem is not None:
+ mposition = fence_elem.findMethod(methodname)
+ if mposition is not None and mposition < len(levels) - 1:
+ fence_elem.moveMethodDown(mposition)
+ self.model.setModified(True)
+ rh.update_cluster_conf(self.model)
+ log.info('User "%s" moved down fence method "%s" for node "%s" in cluster "%s"'
+ % (self.username, methodname, cur_nodename, self.name))
+
+ elif command == "AddFence":
+ node = self.model.retrieveNodeByName(cur_nodename)
levels = node.getFenceMethods()
fence_method = Method()
fence_method.addAttribute('name', '1')
if len(levels) > 0:
- methodname = kw['methodname']
+ methodname = kw.get('methodname')
fence_methods = node.getFenceMethods()
for i in fence_methods:
if i.getAttribute("name") == methodname:
fence_method = i
break
- parent_fencedev = kw['parent_fencedev'].replace('fd_', '', 1)
+ parent_fencedev = kw.get('parent_fencedev').replace('fd_', '', 1)
- retcode, retobj, retunfence = validate_fenceinstance(parent_fencedev, kw['fence_type'], **kw)
+ retcode, retobj, retunfence = validate_fenceinstance(parent_fencedev, kw.get('fence_type'), **kw)
if retcode is True:
# Add unfence section if requested.
if retunfence is not None:
@@ -251,21 +267,27 @@ class IndividualClusterController(BaseController):
if len(levels) == 0:
fence_node = node.getFenceNode()
fence_node.addChild(fence_method)
- flash(_('Updating fence settings'))
+
+ flash(_('Updating fence settings for node "%s"' % cur_nodename))
+ log.info('User "%s" added a fence instance to fence method "%s" for node "%s" in cluster "%s"'
+ % (self.username, methodname, cur_nodename, self.name))
self.model.setModified(True)
rh.update_cluster_conf(self.model)
else:
msgs = retobj
if msgs and len(msgs) > 0:
- flash(', '.join(msgs), status="error")
+ err_msg_str = ', '.join(msgs)
+ flash('%s' % err_msg_str, status="error")
+ log.error('User "%s" failed to add a fence instance to fence method "%s" for node "%s" in cluster "%s": %s'
+ % (self.username, methodname, cur_nodename, self.name, err_msg_str))
- if command == "EditFence":
- node = self.model.retrieveNodeByName(kw.get("node"))
+ elif command == "EditFence":
+ node = self.model.retrieveNodeByName(cur_nodename)
instance_id = kw.get("instance_id")
- (method_num,sep,fence_instance_id) = instance_id.partition('-')
+ (method_num, sep, fence_instance_id) = instance_id.partition('-')
# Create the fence device, and if it succeeds replace the old one with the new one
- retcode, retobj, retunfence = validate_fenceinstance(kw["fencedev"], kw['fence_type'], **kw)
+ retcode, retobj, retunfence = validate_fenceinstance(kw.get('fencedev'), kw.get('fence_type'), **kw)
if retcode is True:
# Add 'unfence' section if requested, remove it otherwise.
old_device = node.getFenceNode().children[int(method_num)].children[int(fence_instance_id)]
@@ -297,19 +319,23 @@ class IndividualClusterController(BaseController):
unfence.addChild(retunfence)
# ---
node.getFenceNode().children[int(method_num)].children[int(fence_instance_id)] = retobj
+ log.info('User "%s" edited "%s" fence instance of fence method "%s" for node "%s" in cluster "%s"'
+ % (self.username, kw.get('fencedev'), method_num, cur_nodename, self.name))
self.model.setModified(True)
rh.update_cluster_conf(self.model)
- if command == "AddFenceMethod":
- node = self.model.retrieveNodeByName(kw.get("node"))
+ elif command == "AddFenceMethod":
+ node = self.model.retrieveNodeByName(cur_nodename)
methodname = kw.get("newmethodname")
method = Method()
- method.addAttribute("name",methodname)
+ method.addAttribute("name", methodname)
node.getFenceNode().addChild(method)
+ log.info('User "%s" added fence method "%s" to node "%s" in cluster "%s"'
+ % (self.username, methodname, cur_nodename, self.name))
self.model.setModified(True)
rh.update_cluster_conf(self.model)
- redirect('%s%s' % (tmpl_context.cluster_url, kw.get('node')))
+ redirect('%s%s' % (tmpl_context.cluster_url, cur_nodename))
# This processes all of the commands that we can apply to a node
@expose("luci.templates.node")
@@ -471,7 +497,7 @@ class IndividualClusterController(BaseController):
# For IP resources there is no name, just an IP Address
# and to make IP addresses work you need to add a .html
if res_type == 'ip':
- res_name = kw['address']
+ res_name = kw.get('address')
redir_fmt = '%s/%s.html'
else:
res_name = kw.get('resourcename')
diff --git a/luci/lib/ClusterConf/Fence.py b/luci/lib/ClusterConf/Fence.py
index 1fafbae..36ea9a4 100644
--- a/luci/lib/ClusterConf/Fence.py
+++ b/luci/lib/ClusterConf/Fence.py
@@ -13,3 +13,18 @@ class Fence(TagObject):
def __init__(self):
TagObject.__init__(self)
self.TAG_NAME = TAG_NAME
+
+ def findMethod(self, method_name):
+ for m in xrange(len(self.children)):
+ cur_m = self.children[m]
+ if cur_m.getAttribute("name") == method_name:
+ return m
+ return None
+
+ def moveMethodUp(self, method_index):
+ cur_method = self.children.pop(method_index)
+ self.children.insert(method_index - 1, cur_method)
+
+ def moveMethodDown(self, method_index):
+ cur_method = self.children.pop(method_index)
+ self.children.insert(method_index + 1, cur_method)
diff --git a/luci/public/js/node.js b/luci/public/js/node.js
index 672cff2..9262b42 100644
--- a/luci/public/js/node.js
+++ b/luci/public/js/node.js
@@ -63,3 +63,36 @@ function gen_fence_xml(form) {
}
form.submit();
}
+
+function fence_method_remove(fence_form_id, method_name) {
+ var form_elem = document.getElementById(fence_form_id);
+ if (form_elem && method_name) {
+ $(form_elem.method_to_remove).attr('value', method_name);
+ form_elem.submit();
+ }
+}
+
+function fence_method_moveup(fence_form_id, method_name) {
+ var form_elem = document.getElementById(fence_form_id);
+ if (form_elem && method_name) {
+ $(form_elem.moveup_method).attr('value', method_name);
+ form_elem.submit();
+ }
+}
+
+function fence_method_movedown(fence_form_id, method_name) {
+ var form_elem = document.getElementById(fence_form_id);
+ if (form_elem && method_name) {
+ $(form_elem.movedown_method).attr('value', method_name);
+ form_elem.submit();
+ }
+}
+
+function fence_instance_remove(fence_form_id, method_name, instance_name) {
+ var form_elem = document.getElementById(fence_form_id);
+ if (form_elem && method_name && instance_name) {
+ $(form_elem.fenceinst_to_remove_method).attr('value', method_name);
+ $(form_elem.fenceinst_to_remove).attr('value', instance_name);
+ form_elem.submit();
+ }
+}
diff --git a/luci/templates/node.html b/luci/templates/node.html
index 772bad1..e06e53a 100644
--- a/luci/templates/node.html
+++ b/luci/templates/node.html
@@ -52,7 +52,7 @@
</div>
</py:if>
- <form action="${'%snodes_cmd' % tmpl_context.cluster_url}" method="post">
+ <form action="${tg.url('%snodes_cmd' % tmpl_context.cluster_url)}" method="post">
<div class="sectionblock">
<xi:include href="submenu.html"/>
<div id="toolbar">
@@ -215,10 +215,10 @@
<div id="details_header">
<h3 py:content="name"/>
<div id="details_header_buttons">
- <a href="${tg.url('nodes_cmd?command=Reboot' + '&name=' + name)}" id="dh_reboot" title="reboot"><span class="hide">reboot</span></a>
- <a href="${tg.url('nodes_cmd?command=Join+Cluster' + '&name=' + name)}" id="dh_join" title="join"><span class="hide">join</span></a>
- <a href="${tg.url('nodes_cmd?command=Leave+Cluster' + '&name=' + name)}" id="dh_leave" title="leave cluster"><span class="hide">leave cluster</span></a>
- <a href="${tg.url('nodes_cmd?command=Delete' + '&name=' + name)}" id="dh_delete" title="delete"><span class="hide">delete</span></a>
+ <a href="${tg.url('%snodes_cmd?command=Reboot&name=%s' % (tmpl_context.cluster_url, name))}" id="dh_reboot" title="reboot"><span class="hide">reboot</span></a>
+ <a href="${tg.url('%snodes_cmd?command=Join+Cluster&name=%s' % (tmpl_context.cluster_url, name))}" id="dh_join" title="join"><span class="hide">join</span></a>
+ <a href="${tg.url('%snodes_cmd?command=Leave+Cluster&name=%s' % (tmpl_context.cluster_url, name))}" id="dh_leave" title="leave cluster"><span class="hide">leave cluster</span></a>
+ <a href="${tg.url('%snodes_cmd?command=Delete&name=%s' % (tmpl_context.cluster_url, name))}" id="dh_delete" title="delete"><span class="hide">delete</span></a>
</div>
<span class="details_header_info_label">Status</span>
<span class="details_header_info" py:choose="details['clustered']">
@@ -232,7 +232,7 @@
<div class="details_section">
<h4>Properties</h4>
<div class="details_inner">
- <form action="${'%snodes_cmd?command=Update+Attributes' % tmpl_context.cluster_url}" method="post"
+ <form action="${tg.url('%snodes_cmd?command=Update+Attributes' % tmpl_context.cluster_url)}" method="post"
py:with="node_data = cluster_data.getNodeByName(name);
nodedbobj = db_helpers.get_cluster_node(tmpl_context.cluster_name, name)">
<input type="hidden" name="name" value="${name}"/>
@@ -320,7 +320,7 @@
fenceinst_num = 0
?>
<div class="details_inner">
- <form method="post" name="node_fence_form" action="${tg.url('nodes_fence_cmd')}">
+ <form method="post" name="node_fence_form" id="node_fence_form" action="${tg.url('%snodes_fence_cmd' % tmpl_context.cluster_url)}">
<input type="hidden" name="method_to_remove" value=""/>
<input type="hidden" name="fenceinst_to_remove" value=""/>
<input type="hidden" name="fenceinst_to_remove_method" value=""/>
@@ -337,16 +337,16 @@
<tbody>
<py:for each="order,(method,instances) in enumerate(fence_info)"
py:with="cur_dev_id=method">
- <tr>
- <td colspan="2">${method.getName().replace(' ',' ')}</td>
+ <tr py:with="method_name = method.getName()">
+ <td colspan="2">${method_name.replace(' ',' ')}</td>
<td align="right">
- <a py:if="order != 0" href="" onclick="document.node_fence_form.moveup_method.value = "${method.getName()}";document.node_fence_form.submit();return false;">Move Up</a>
+ <a py:if="order != 0" href="" onclick="fence_method_moveup('node_fence_form', '${method_name}')">Move Up</a>
</td>
<td align="right">
- <a py:if="order != len(fence_info)-1" href="" onclick="document.node_fence_form.movedown_method.value = "${method.getName()}";document.node_fence_form.submit();return false;">Move Down</a>
+ <a py:if="order != len(fence_info)-1" href="" onclick="fence_method_movedown('node_fence_form', '${method_name}')">Move Down</a>
</td>
<td align="right">
- <a href="" onclick="document.node_fence_form.method_to_remove.value = "${method.getName()}";document.node_fence_form.submit();return false;">Remove</a>
+ <a href="" onclick="fence_method_remove('node_fence_form', '${method_name}')">Remove</a>
</td>
</tr>
@@ -371,7 +371,7 @@
<div class="hidden">
<div id="edit_fencedev_dialog_${instance_id}">
<form name="filler"/>
- <form name="edit_fence_device_${instance_id}" method="post" action="${tg.url('nodes_fence_cmd?command=EditFence')}">
+ <form name="edit_fence_device_${instance_id}" method="post" action="${tg.url('%snodes_fence_cmd?command=EditFence' % tmpl_context.cluster_url)}">
<input type="hidden" name="node" value="${name}"/>
<input type="hidden" name="instance_id" value="${instance_id}"/>
<input type="hidden" name="fencedev" value="${agent_alias}"/>
@@ -409,7 +409,7 @@
<td><a href="#" onclick="$('#edit_fencedev_dialog_${instance_id}').dialog('open')">${agent_alias}</a></td>
<td>${agent_name}</td>
<td align="right">
- <a href="" onclick="document.node_fence_form.fenceinst_to_remove_method.value = "${method.getName()}";document.node_fence_form.fenceinst_to_remove.value = "${fenceinst_num}";document.node_fence_form.submit();return false;">
+ <a href="" onclick="fence_instance_remove('node_fence_form', '${method.getName()}', '${fenceinst_num}')">
<img src="/images/delete-grey.png"/>
</a>
</td>
@@ -429,7 +429,7 @@
</py:for>
<tr>
<td colspan="3">
- <input type="button" py:if="len(cluster_data.getFenceDevices()) > 0" value="Add Fence Instance" class="button small silver" onclick="$(create_fence_device.methodname).attr('value','${method.getName()}');$('#create_fencedev_dialog').dialog('open')"/>
+ <input type="button" py:if="len(cluster_data.getFenceDevices()) > 0" value="Add Fence Instance" class="button small silver" onclick="$('#create_fence_device_form input[name=methodname]').attr('value','${method.getName()}');$('#create_fencedev_dialog').dialog('open')"/>
</td>
</tr>
</table>
@@ -483,7 +483,7 @@
<div py:if="name is not None" class="hidden">
<div id="create_fencedev_dialog">
- <form name="create_fence_device" method="post" action="${tg.url('nodes_fence_cmd?command=AddFence')}">
+ <form name="create_fence_device" id="create_fence_device_form" method="post" action="${tg.url('%snodes_fence_cmd?command=AddFence' % tmpl_context.cluster_url)}">
<input type="hidden" name="node" value="${name}"/>
<input type="hidden" name="methodname" value=""/>
${fence_device_select(cluster_data, 'fence_form_area')}
@@ -498,7 +498,7 @@
</div>
<div class="hidden">
<div id="create_fencemethod_dialog">
- <form name="create_fence_method" method="post" action="${tg.url('nodes_fence_cmd?command=AddFenceMethod')}">
+ <form name="create_fence_method" method="post" action="${tg.url('%snodes_fence_cmd?command=AddFenceMethod' % tmpl_context.cluster_url)}">
<input type="hidden" name="node" value="${name}"/>
Method Name:<input type="text" name="newmethodname" value="Method"/>
<div class="row">
13 years, 3 months
[luci] Fix an error case that can occur when a host's FQDN differs from its node name
by Ryan McCabe
commit 1b557a2ee84128e4691a5f2b2666f229ba0e654e
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Fri Feb 11 10:59:10 2011 -0500
Fix an error case that can occur when a host's FQDN differs from its node name
luci/lib/cluster_status.py | 10 ++++++++++
luci/templates/failover.html | 2 +-
luci/templates/node.html | 6 +++---
3 files changed, 14 insertions(+), 4 deletions(-)
---
diff --git a/luci/lib/cluster_status.py b/luci/lib/cluster_status.py
index b7fa997..4609a8e 100644
--- a/luci/lib/cluster_status.py
+++ b/luci/lib/cluster_status.py
@@ -97,3 +97,13 @@ class ClusterStatus:
self.nodes[ss.nodename].services[ss.name] = ss
except Exception, e:
log.exception('Error parsing service list')
+
+ def getNodeStatus(self, nodename):
+ ns = self.nodes.get(nodename)
+ if ns:
+ return ns
+
+ for n in self.nodes.keys():
+ if n in nodename or nodename in n:
+ return self.nodes[n]
+ return NodeStatus(None)
diff --git a/luci/templates/failover.html b/luci/templates/failover.html
index 516f9f7..cd43ae8 100644
--- a/luci/templates/failover.html
+++ b/luci/templates/failover.html
@@ -180,7 +180,7 @@
<tr py:for="node_dict in cluster_data.getNodes()" class="grid_row"
py:with="node = node_dict.getName()">
<!--! Branch according to the status of the node. -->
- <py:choose test="cluster_status.nodes[node].clustered">
+ <py:choose test="cluster_status.getNodeStatus(node).clustered">
<!--! 1) Node is active. -->
<py:when test="'true'">
<td class="icon"></td>
diff --git a/luci/templates/node.html b/luci/templates/node.html
index ff317d6..772bad1 100644
--- a/luci/templates/node.html
+++ b/luci/templates/node.html
@@ -209,7 +209,7 @@
</div>
</py:when>
- <py:otherwise py:with="details = cluster_status.nodes[name]">
+ <py:otherwise py:with="details = cluster_status.getNodeStatus(name)">
<!--! DETAILS - header section. -->
<div id="details_header">
@@ -234,7 +234,7 @@
<div class="details_inner">
<form action="${'%snodes_cmd?command=Update+Attributes' % tmpl_context.cluster_url}" method="post"
py:with="node_data = cluster_data.getNodeByName(name);
- nodedbobj = db_helpers.get_cluster_node(tmpl_context.cluster_name, name)">
+ nodedbobj = db_helpers.get_cluster_node(tmpl_context.cluster_name, name)">
<input type="hidden" name="name" value="${name}"/>
<input type="submit" value="Update Properties" class="float_button"/>
<table id="node_tattr">
@@ -242,7 +242,7 @@
<td class="node_tattr_caption">
Number of votes
</td><td class="node_tattr_input">
- <input type="text" name="votes" py:attrs="{'value': node_data.getVotes()}"/>
+ <input type="text" name="votes" py:attrs="{'value': node_data and node_data.getVotes()}"/>
</td>
</tr>
<tr>
13 years, 3 months
[luci] Fix bz#616239: Need option to completely destroy cluster
by Ryan McCabe
commit e6d9224529de4cd62f901bad661abfa85bc08a53
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Fri Feb 4 07:00:56 2011 -0500
Fix bz#616239: Need option to completely destroy cluster
luci/controllers/cluster.py | 19 +++++++++++++------
.../validate_create_cluster_form.py | 2 +-
2 files changed, 14 insertions(+), 7 deletions(-)
---
diff --git a/luci/controllers/cluster.py b/luci/controllers/cluster.py
index 53f8913..524ceed 100644
--- a/luci/controllers/cluster.py
+++ b/luci/controllers/cluster.py
@@ -51,7 +51,6 @@ class ClusterController(BaseController):
@expose('luci.templates.cluster_list')
def delete_cmd(self, command=None, **kw):
- cur_list = []
if 'MultiAction' in kw:
command = kw['MultiAction']
for key, element in kw.items():
@@ -354,11 +353,19 @@ class IndividualClusterController(BaseController):
status='info')
rh.cluster_node_stop(self.name, cur_list)
elif command == 'Delete':
- log.info('User "%s" deleted nodes "%s" from cluster "%s"'
- % (self.username, ', '.join(cur_list), self.name))
- flash(_("Deleting nodes: %s") % ', '.join(cur_list),
- status='info')
- rh.cluster_node_delete(self.name, self.model, cur_list)
+ if len(cur_list) == len(self.model.getNodes()):
+ log.info('User "%s" deleted cluster "%s"'
+ % (self.username, self.name))
+ flash(_("Deleting cluster: %s") % self.name, status='info')
+ if rh.cluster_delete(self.name):
+ db_remove_cluster(self.name)
+ redirect('/cluster/')
+ else:
+ log.info('User "%s" deleted nodes "%s" from cluster "%s"'
+ % (self.username, ', '.join(cur_list), self.name))
+ flash(_("Deleting nodes: %s") % ', '.join(cur_list),
+ status='info')
+ rh.cluster_node_delete(self.name, self.model, cur_list)
elif command == 'Update Attributes':
vret = vcp.validate_node_prop_settings_form(cur_list[0], self.model, **kw)
if vret[0] is True:
diff --git a/luci/widget_validators/validate_create_cluster_form.py b/luci/widget_validators/validate_create_cluster_form.py
index 2f8867a..679e902 100644
--- a/luci/widget_validators/validate_create_cluster_form.py
+++ b/luci/widget_validators/validate_create_cluster_form.py
@@ -153,7 +153,7 @@ def validate_create_cluster_form(self, **kw):
cluster_db_obj.tasks = task_db_obj.values()
for i in node_db_obj.iterkeys():
cur_task_obj = task_db_obj.get(i)
- if cur_task:
+ if cur_task_obj:
node_db_obj[i].tasks = [ cur_task_obj ]
DBSession.add(cluster_db_obj)
except Exception, e:
13 years, 3 months
[luci] Fix rhbz#605932: Missing "reset to defaults" button in qdisk configuration
by Ryan McCabe
commit 24c7e65e4bb8273060e02821b0ef917ad01c357a
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Thu Feb 3 13:47:26 2011 -0500
Fix rhbz#605932: Missing "reset to defaults" button in qdisk configuration
luci/public/js/qdisk.js | 23 +++++++++++++++++++++++
luci/templates/configure.html | 13 +++++++++----
2 files changed, 32 insertions(+), 4 deletions(-)
---
diff --git a/luci/public/js/qdisk.js b/luci/public/js/qdisk.js
index b736143..2101bdf 100644
--- a/luci/public/js/qdisk.js
+++ b/luci/public/js/qdisk.js
@@ -130,3 +130,26 @@ function enableChildrenInput(parent_name) {
}
oldInput = null;
}
+
+function reset_qdisk_vals(num_nodes) {
+ var interval = $('#qdisk_interval');
+ var votes = $('#qdisk_votes');
+ var tko = $('#qdisk_tko');
+ var min_score = $('#qdisk_min_score');
+
+ if (interval) {
+ $(interval).val('1');
+ }
+
+ if (votes) {
+ $(votes).val('');
+ }
+
+ if (tko) {
+ $(tko).val('');
+ }
+
+ if (min_score) {
+ $(min_score).val('');
+ }
+}
diff --git a/luci/templates/configure.html b/luci/templates/configure.html
index 705cf27..db2891d 100644
--- a/luci/templates/configure.html
+++ b/luci/templates/configure.html
@@ -177,19 +177,19 @@
<div id="qdisk_config">
<div class="row"><label>Interval</label>
- <input name="interval" type="text" class="text"
+ <input name="interval" type="text" class="text" id="qdisk_interval"
py:attrs="(quorumd_ptr and {'value': quorumd_ptr.getAttribute('interval')or ''}) or {'disabled':'disabled'}" />
</div>
<div class="row"><label>Votes</label>
- <input name="votes" type="text" class="text"
+ <input name="votes" type="text" class="text" id="qdisk_votes"
py:attrs="(quorumd_ptr and {'value': quorumd_ptr.getAttribute('votes') or ''}) or {'disabled':'disabled'}" />
</div>
<div class="row"><label>TKO</label>
- <input name="tko" type="text" class="text"
+ <input name="tko" type="text" class="text" id="qdisk_tko"
py:attrs="(quorumd_ptr and {'value': quorumd_ptr.getAttribute('tko') or ''}) or {'disabled':'disabled'}" />
</div>
<div class="row"><label>Minimum Score</label>
- <input name="min_score" type="text" class="text"
+ <input name="min_score" type="text" class="text" id="qdisk_min_score"
py:attrs="(quorumd_ptr and {'value': quorumd_ptr.getAttribute('min_score') or ''}) or {'disabled':'disabled'}" />
</div>
<fieldset>
@@ -275,6 +275,11 @@
<td><input type="button" class="button small silver" value="Add Another Heuristic" onclick="add_qdisk_heuristic()" /></td>
</tr>
</fieldset>
+ <div class="row">
+ <input type="button" class="button small silver"
+ onclick="reset_qdisk_vals()" value="Reset values to defaults"
+ py:attrs="not quorumd_ptr and {'disabled':'disabled'} or {}" />
+ </div>
</div>
<div class="row"><input type="submit" class="button formsubmit blue" value="Apply"/>
</div>
13 years, 3 months
[luci] Fix rhbz#666971: Disable updates to static routes by RHCS IP tooling
by Ryan McCabe
commit 083f9ab542f67e10dbf8f4fc3b24b0e0147167ab
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Thu Feb 3 13:47:07 2011 -0500
Fix rhbz#666971: Disable updates to static routes by RHCS IP tooling
luci/templates/resource_list.html | 9 +++++++++
luci/widget_validators/validate_resource.py | 1 +
2 files changed, 10 insertions(+), 0 deletions(-)
---
diff --git a/luci/templates/resource_list.html b/luci/templates/resource_list.html
index 868b0fb..6fcfcde 100644
--- a/luci/templates/resource_list.html
+++ b/luci/templates/resource_list.html
@@ -61,6 +61,8 @@
<div py:def="ip_resource(res, form_id, parent_id, isref)" name="IP" id="ip_resource" class="row rescfg"
py:with="global_resource = res and isref != 0;
+ cluster_os = tmpl_context.cluster.get_OS();
+ os_version = tmpl_context.cluster.get_OSVersion();
ip_address = res and res.getAttribute('address').split('/') or []"
py:attrs="res and {'id': 'global_res_%s' % res.getName(), 'name': res.getName()}">
<input name="global" type="hidden" value="1" py:if="global_resource"/>
@@ -93,6 +95,13 @@
py:attrs="res and {'checked': res.getAttribute('monitor_link') and 'checked' or None, 'disabled':global_resource and 'disabled' or None} or {}" />
</td>
</tr>
+ <tr py:if="cluster_os != 'RHEL' or os_version > 6.0">
+ <td>Disable updates to static routes</td>
+ <td>
+ <input type="checkbox" class="checkbox" name="disable_rdisc"
+ py:attrs="res and {'checked': res.getAttribute('disable_rdisc') and 'checked' or None, 'disabled':global_resource and 'disabled' or None} or {}" />
+ </td>
+ </tr>
<tr>
<td>Number of seconds to sleep after removing an IP address</td>
<td>
diff --git a/luci/widget_validators/validate_resource.py b/luci/widget_validators/validate_resource.py
index a5f845c..dddc91b 100644
--- a/luci/widget_validators/validate_resource.py
+++ b/luci/widget_validators/validate_resource.py
@@ -107,6 +107,7 @@ def addIp(res, rname, model, **kw):
params = (
('address', _('IP address'), True, None),
('monitor_link', _('Monitor Link'), False, None),
+ ('disable_rdisc', _('Disable updates to static routes'), False, None),
('sleeptime', _('Sleep time'), False, None)
)
errors = config_resource(params, res, rname, **kw)
13 years, 3 months
[luci] Fix rhbz#624558: RFE: replace broadcast option with udpu
by Ryan McCabe
commit 78f9f3c7c7737a896fd77128deba4f3b15f9586a
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Thu Feb 3 13:46:02 2011 -0500
Fix rhbz#624558: RFE: replace broadcast option with udpu
luci/controllers/cluster.py | 10 +++++
luci/lib/ClusterConf/Cman.py | 14 ++++++++
luci/lib/ClusterConf/ModelBuilder.py | 41 ++++++++++++++++++----
luci/lib/app_globals.py | 1 +
luci/lib/ricci_communicator.py | 32 +++++++++---------
luci/templates/configure.html | 27 +++++++++++----
luci/widget_validators/validate_cluster_prop.py | 20 +++++-----
7 files changed, 104 insertions(+), 41 deletions(-)
---
diff --git a/luci/controllers/cluster.py b/luci/controllers/cluster.py
index 23a7248..53f8913 100644
--- a/luci/controllers/cluster.py
+++ b/luci/controllers/cluster.py
@@ -80,6 +80,7 @@ class IndividualClusterController(BaseController):
self.status = None
self.version = None
self.OS = None
+ self.OSVersion = None
self.name = name
self.data = iccdata
@@ -139,6 +140,15 @@ class IndividualClusterController(BaseController):
return self.OS
+ def get_OSVersion(self):
+ if not self.OSVersion:
+ self.OS = app_globals.DEFAULT_OS_VERSION
+ m = self.get_model()
+ if m:
+ self.OSVersion = m.getOSVersion()
+
+ return self.OSVersion
+
@expose("luci.templates.node")
def nodes(self):
db = get_cluster_db_obj(self.name)
diff --git a/luci/lib/ClusterConf/Cman.py b/luci/lib/ClusterConf/Cman.py
index 2f66a19..71ace89 100644
--- a/luci/lib/ClusterConf/Cman.py
+++ b/luci/lib/ClusterConf/Cman.py
@@ -139,3 +139,17 @@ class Cman(TagObject):
def delDisableOpenAIS(self):
return self.removeAttribute('disable_openais')
+
+ def getTransport(self):
+ return self.getAttribute('transport')
+
+ def delTransport(self):
+ return self.removeAttribute('transport')
+
+ def setTransport(self, val):
+ if not val:
+ return self.delTransport()
+ val = val.lower()
+ if not val in ('udp', 'udpb', 'udpu', 'rdma'):
+ raise Exception, "Invalid transport: %s" % val
+ return self.addAttribute('transport', val)
diff --git a/luci/lib/ClusterConf/ModelBuilder.py b/luci/lib/ClusterConf/ModelBuilder.py
index bcca786..17ec4b6 100644
--- a/luci/lib/ClusterConf/ModelBuilder.py
+++ b/luci/lib/ClusterConf/ModelBuilder.py
@@ -133,7 +133,7 @@ TAGNAMES = {
}
class ModelBuilder:
- def __init__(self, conf_xml_obj, cluster_version=(3, 'Fedora')):
+ def __init__(self, conf_xml_obj, cluster_version=(3, 'Fedora', None)):
if conf_xml_obj is None:
raise Exception, 'No cluster configuration'
self.errors = False
@@ -160,7 +160,7 @@ class ModelBuilder:
self.mcast_ptr = None
self.lockspace_ptr = None
self.unknown_elements = list()
- (self.cluster_version, self.cluster_os) = cluster_version
+ (self.cluster_version, self.cluster_os, self.os_version) = cluster_version
self.parent = conf_xml_obj
self.object_tree = self.buildModel(None)
@@ -183,6 +183,13 @@ class ModelBuilder:
def getClusterOS(self):
return self.cluster_os
+ def getOSVersion(self):
+ try:
+ return float(self.os_version)
+ except:
+ pass
+ return 0.0
+
def buildModel(self, parent_node, parent_object=None):
if parent_node is None:
parent_node = self.parent
@@ -819,6 +826,7 @@ class ModelBuilder:
return self.logging_ptr
def set_cluster_broadcast(self):
+ self.del_cluster_udpu()
if self.del_cluster_multicast() is False:
return False
return self.cman_ptr.setBroadcast(True)
@@ -834,24 +842,41 @@ class ModelBuilder:
return False
return self.cman_ptr.getBroadcast()
+ def del_cluster_multicast(self):
+ if self.cman_ptr is None:
+ return True
+
+ if self.mcast_ptr is not None:
+ self.cman_ptr.removeChild(self.mcast_ptr)
+ self.mcast_ptr = None
+ self.isModified = True
+ return True
+
def set_cluster_multicast(self, mcast_addr=None):
+ self.del_cluster_multicast()
self.del_cluster_broadcast()
+ self.del_cluster_udpu()
if mcast_addr is not None:
if self.mcast_ptr is None:
self.mcast_ptr = Multicast.Multicast()
self.cman_ptr.addChild(self.mcast_ptr)
self.mcast_ptr.setAddr(mcast_addr)
- def del_cluster_multicast(self):
+ def get_cluster_udpu(self):
if self.cman_ptr is None:
- return True
+ return False
+ return self.cman_ptr.getTransport() == 'udpu'
- if self.mcast_ptr is not None:
- self.cman_ptr.removeChild(self.mcast_ptr)
- self.mcast_ptr = None
- self.isModified = True
+ def set_cluster_udpu(self):
+ self.del_cluster_broadcast()
+ self.del_cluster_multicast()
+ self.cman_ptr.setTransport('udpu')
return True
+ def del_cluster_udpu(self):
+ self.cman_ptr.delTransport()
+ self.isModified = True
+
def check_fence_daemon(self):
if self.fence_daemon_ptr is None:
self.fence_daemon_ptr = FenceDaemon.FenceDaemon()
diff --git a/luci/lib/app_globals.py b/luci/lib/app_globals.py
index 334f874..776650c 100644
--- a/luci/lib/app_globals.py
+++ b/luci/lib/app_globals.py
@@ -22,6 +22,7 @@ class Globals(object):
"""
DEFAULT_CLUSTER_VERSION = 3
DEFAULT_CLUSTER_OS = 'Fedora'
+ DEFAULT_OS_VERSION = None
DEFAULT_RICCI_PORT = 11111
def __init__(self):
diff --git a/luci/lib/ricci_communicator.py b/luci/lib/ricci_communicator.py
index c0482ce..1c41479 100644
--- a/luci/lib/ricci_communicator.py
+++ b/luci/lib/ricci_communicator.py
@@ -33,7 +33,7 @@ class RicciCommunicator:
self.__timeout_short = 6
self.__timeout_long = 600
- self.__cluster_version = (-1, 'unknown')
+ self.__cluster_version = (-1, 'unknown', None)
# Which file to use as certificate for communicating to ricci
# has to be specified in the configuration
@@ -609,31 +609,31 @@ def extract_module_status(batch_xml, module_num=1):
def resolve_cluster_version(uname_str):
if 'Santiago' in uname_str:
- return (3, 'RHEL')
+ return (3, 'RHEL', uname_str[uname_str.find('6.'):].split(' ')[0])
elif 'Tikanga' in uname_str:
- return (2, 'RHEL')
+ return (2, 'RHEL', uname_str[uname_str.find('5.'):].split(' ')[0])
elif 'Nahant' in uname_str:
- return (1, 'RHEL')
+ return (1, 'RHEL', uname_str[uname_str.find('4.'):].split(' ')[0])
elif 'Laughlin' in uname_str:
- return (3, 'Fedora')
+ return (3, 'Fedora', '14')
elif 'Goddard' in uname_str:
- return (3, 'Fedora')
+ return (3, 'Fedora', '13')
elif 'Constantine' in uname_str:
- return (3, 'Fedora')
+ return (3, 'Fedora', '12')
elif 'Leonidas' in uname_str:
- return (3, 'Fedora')
+ return (3, 'Fedora', '11')
elif 'Cambridge' in uname_str:
- return (3, 'Fedora')
+ return (3, 'Fedora', '10')
elif 'Sulphur' in uname_str:
- return (3, 'Fedora')
+ return (3, 'Fedora', '9')
elif 'Werewolf' in uname_str:
- return (2, 'Fedora')
+ return (2, 'Fedora', '8')
elif 'Moonshine' in uname_str:
- return (2, 'Fedora')
+ return (2, 'Fedora', '7')
elif 'Zod' in uname_str:
- return (2, 'Fedora')
+ return (2, 'Fedora', '6')
elif 'Bordeaux' in uname_str:
- return (2, 'Fedora')
+ return (2, 'Fedora', '5')
elif 'Stentz' in uname_str:
- return (1, 'Fedora')
- return (-1, 'unknown')
+ return (1, 'Fedora', '4')
+ return (-1, 'unknown', None)
diff --git a/luci/templates/configure.html b/luci/templates/configure.html
index 1b9bbb6..705cf27 100644
--- a/luci/templates/configure.html
+++ b/luci/templates/configure.html
@@ -59,7 +59,9 @@
<script type="text/javascript">
</script>
- <div id="tabs">
+ <div id="tabs"
+ py:with="cluster_os = tmpl_context.cluster.get_OS();
+ os_version = tmpl_context.cluster.get_OSVersion()">
<ul>
<li><a href="#tabs-1">General</a></li>
<li><a href="#tabs-2">Fence Daemon</a></li>
@@ -107,27 +109,38 @@
<div class="row"
py:with="
uses_broadcast = cluster_data and cluster_data.get_cluster_broadcast();
+ uses_udpu = cluster_data and cluster_data.get_cluster_udpu();
multicast_addr = cluster_data and cluster_data.getMcastAddr()">
<input name="multicast" value="multicast" class="radio" type="radio"
- py:attrs="(uses_broadcast is not True and multicast_addr is None) and {'checked': 'checked'} or {}"/>
+ py:attrs="(uses_broadcast is not True and uses_udpu is not True and multicast_addr is None) and {'checked': 'checked'} or {}"/>
<label class="choice">Let cluster choose the multicast address</label>
<br />
<input name="multicast" value="multicast_manual" class="radio" type="radio"
- py:attrs="(uses_broadcast is not True and multicast_addr is not None) and {'checked': 'checked'} or {}"/>
+ py:attrs="(uses_broadcast is not True and uses_udpu is not True and multicast_addr is not None) and {'checked': 'checked'} or {}"/>
<label class="choice">Specify the multicast address manually</label>
<br />
<label class="indent">Multicast address</label>
<input type="text" class="text" name="mcast_address" value="${cluster_data and cluster_data.getMcastAddr()}"/>
<br />
- <input name="multicast" value="broadcast" class="radio" type="radio"
- py:attrs="uses_broadcast is True and {'checked': 'checked'} or {}"/>
- <label class="choice">Use broadcast</label>
+
+ <py:if test="cluster_os != 'RHEL' or os_version < 6.1">
+ <input name="multicast" value="broadcast" class="radio" type="radio"
+ py:attrs="uses_broadcast is True and {'checked': 'checked'} or {}"/>
+ <label class="choice">Use broadcast</label>
+ <br/>
+ </py:if>
+
+ <py:if test="cluster_os != 'RHEL' or os_version > 6.0">
+ <input name="multicast" value="udpu" class="radio" type="radio"
+ py:attrs="uses_udpu is True and {'checked': 'checked'} or {}"/>
+ <label class="choice">Use UDP unicast (UDPU)</label>
+ </py:if>
+
</div>
<div class="row"><div id="button"><button class="button small silver">Show Advanced Properties</button></div>
</div>
-
<div id="advanced"
py:with="totem=cluster_data and cluster_data.getTotemPtr()">
<div class="row"><label>Token Timeout (ms)</label><input name="token_timeout" class="text" type="text" py:attrs="totem and { 'value': totem.getTokenTimeout() } or {'value': totem_defaults.get('token')}"/></div>
diff --git a/luci/widget_validators/validate_cluster_prop.py b/luci/widget_validators/validate_cluster_prop.py
index 9675c14..9fe582a 100644
--- a/luci/widget_validators/validate_cluster_prop.py
+++ b/luci/widget_validators/validate_cluster_prop.py
@@ -42,7 +42,7 @@ def validate_fdom_prop_form(model, **kw):
fdom.addAttribute('ordered', '1')
else:
fdom.addAttribute('ordered', '0')
-
+
restricted = kw.get('restricted')
if restricted:
fdom.addAttribute('restricted', '1')
@@ -77,7 +77,7 @@ def validate_fdom_create_form(model, **kw):
fdom.addAttribute('ordered', '1')
else:
fdom.addAttribute('ordered', '0')
-
+
restricted = kw.get('restricted')
if restricted:
fdom.addAttribute('restricted', '1')
@@ -145,7 +145,7 @@ def validate_fdom_prop_settings_form(model, **kw):
else:
if fdn:
fdom.removeChild(fdn)
-
+
return (len(errors) < 1, {'errors': errors})
def validate_qdisk_prop_form(model, **kw):
@@ -328,7 +328,7 @@ def validate_add_existing(**kw):
if len(errors) != 0:
return (False, {'errors': errors })
-
+
if create_cluster_obj(cluster_name, node_list) is not True:
errors.append(_('Unable to create the database objects for cluster %s') % cluster_name)
else:
@@ -418,7 +418,7 @@ def validate_cluster_config_form(model, **kw):
errors.append(_('Invalid post fail delay: %s') % post_fail_delay)
else:
fd.setPostFailDelay(post_fail_delay)
-
+
post_join_delay = kw.get('post_join_delay')
if post_join_delay:
try:
@@ -427,7 +427,7 @@ def validate_cluster_config_form(model, **kw):
errors.append(_('Invalid post join delay: %s') % post_join_delay)
else:
fd.setPostJoinDelay(post_join_delay)
- if model.getClusterVersion() == 2:
+ if model.getClusterVersion() == 2:
fence_xvmd = kw.get('fence_xvmd')
if not fence_xvmd:
model.delFenceXVM()
@@ -451,13 +451,13 @@ def validate_cluster_config_form(model, **kw):
mcast_addr = kw.get('mcast_address')
if multicast == "multicast":
- model.del_cluster_broadcast()
- model.del_cluster_multicast()
+ model.set_cluster_multicast()
elif multicast == "multicast_manual":
- model.del_cluster_broadcast()
- model.set_cluster_multicast(mcast_addr, None)
+ model.set_cluster_multicast(mcast_addr)
elif multicast == "broadcast":
model.set_cluster_broadcast()
+ elif multicast == "udpu":
+ model.set_cluster_udpu()
else:
return (False, {'errors': [ _('Invalid value for multicast configuration: %s') % multicast]})
13 years, 3 months
[luci] Fix rhbz#639123: Disable action buttons when no nodes are selected
by Ryan McCabe
commit 92b3be8e07cf3c9348304b9e986b08e028fd95d0
Author: Ryan McCabe <rmccabe(a)redhat.com>
Date: Thu Feb 3 13:45:16 2011 -0500
Fix rhbz#639123: Disable action buttons when no nodes are selected
luci/public/css/shared.css | 4 ++++
luci/public/js/shared.js | 17 +++++++++++++++++
luci/templates/cluster_list.html | 12 +++++++++---
luci/templates/failover.html | 7 +++++--
luci/templates/fence.html | 7 +++++--
luci/templates/node.html | 18 ++++++++++++------
luci/templates/resource.html | 3 ++-
luci/templates/service.html | 10 +++++-----
8 files changed, 59 insertions(+), 19 deletions(-)
---
diff --git a/luci/public/css/shared.css b/luci/public/css/shared.css
index 88d82f4..4e9d134 100644
--- a/luci/public/css/shared.css
+++ b/luci/public/css/shared.css
@@ -167,6 +167,10 @@ td.icon, th.icon {
color: #2D2C2C;
}
+.MultiAction[disabled]:hover {
+ cursor: not-allowed;
+}
+
/* specific toolbar's buttons */
/* update */
diff --git a/luci/public/js/shared.js b/luci/public/js/shared.js
index 20c9a0e..7943a20 100644
--- a/luci/public/js/shared.js
+++ b/luci/public/js/shared.js
@@ -69,3 +69,20 @@ function input_to_xml(elem) {
}
return cur_xml_str;
}
+
+function update_multi_action(checkbox_obj) {
+ if (!checkbox_obj || !checkbox_obj.form) {
+ return;
+ }
+ var cur_form = $(checkbox_obj.form);
+ var maction_buttons = $('.MultiAction', cur_form);
+ var maction_items = $('.MultiActionItem', cur_form);
+
+ if (checkbox_obj.checked !== false) {
+ maction_buttons.removeAttr('disabled', 'disabled');
+ } else {
+ if ($(':checked', maction_items).length == 0) {
+ maction_buttons.attr('disabled', 'disabled');
+ }
+ }
+}
diff --git a/luci/templates/cluster_list.html b/luci/templates/cluster_list.html
index 04beeb0..5783c9f 100644
--- a/luci/templates/cluster_list.html
+++ b/luci/templates/cluster_list.html
@@ -26,7 +26,7 @@
<div id="toolbar">
<a href="#" onclick="$('#add_existing_dialog').dialog('open')" class="toolbar_button" id="tb_add">Add</a>
<a href="#" onclick="$('#create_cluster_dialog').dialog('open')" class="toolbar_button" id="tb_create">Create</a>
- <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
+ <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button MultiAction" id="tb_delete" disabled="disabled"/>
</div>
<!--! OVERVIEW SECTION. -->
@@ -49,7 +49,10 @@
py:attrs="not i%2 and {'class': 'even'} or None"
py:with="identifier = entity_name">
<py:if test="hasattr(cluster_data['status'],'quorate')">
- <td class="checkbox"><input type="checkbox" name="${identifier}"/></td>
+ <td class="checkbox">
+ <input type="checkbox" name="${identifier}"
+ onclick="update_multi_action(this)" class="MultiActionItem"/>
+ </td>
<!--! Branch according to the status of the cluster. -->
<py:choose test="cluster_data['status'].quorate">
<!--! 1) Cluster is OK. -->
@@ -94,7 +97,10 @@
</py:choose>
</py:if>
<div py:if="not hasattr(cluster_data['status'],'quorate')">
- <td class="checkbox"><input type="checkbox" name="${identifier}"/></td>
+ <td class="checkbox">
+ <input type="checkbox" name="${identifier}"
+ onclick="update_multi_action(this)" class="MultiActionItem"/>
+ </td>
<td class="main_id">
<a href="${tg.url('/' + page + '/' + entity_name) + '/'}">
<span class="entity_unknown">${entity_name}</span>
diff --git a/luci/templates/failover.html b/luci/templates/failover.html
index d38f4fe..516f9f7 100644
--- a/luci/templates/failover.html
+++ b/luci/templates/failover.html
@@ -22,7 +22,7 @@
<xi:include href="submenu.html"/>
<div id="toolbar">
<a href="#" onclick="$('#create_fdom_dialog').dialog('open')" class="toolbar_button" id="tb_add">Add</a>
- <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
+ <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button MultiAction" id="tb_delete" disabled="disabled"/>
</div>
<!--! OVERVIEW SECTION. -->
@@ -43,7 +43,10 @@
<tr py:for="i, failover_data in enumerate(cluster_data.getFailoverDomains())"
py:with="entity_name=failover_data.getName();identifier=entity_name"
py:attrs="entity_name==name and {'class': 'chosen'} or (not i%2 and {'class': 'even'} or None)">
- <td class="checkbox"><input type="checkbox" name="${identifier}"/></td>
+ <td class="checkbox">
+ <input type="checkbox" name="${identifier}"
+ onclick="update_multi_action(this)" class="MultiActionItem"/>
+ </td>
<td class="icon"></td>
<td class="main_id"><a href="${tg.url(base_url + '/' + entity_name)}">${entity_name}</a></td>
<td class="fdom_tlist_prioritizied">
diff --git a/luci/templates/fence.html b/luci/templates/fence.html
index e08b6c0..372482c 100644
--- a/luci/templates/fence.html
+++ b/luci/templates/fence.html
@@ -21,7 +21,7 @@
<xi:include href="submenu.html"/>
<div id="toolbar">
<a href="#" class="toolbar_button" id="tb_add" onclick="$('#create_fencedev_dialog').dialog('open')">Add</a>
- <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button" id="tb_delete"/>
+ <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button MultiAction" id="tb_delete" disabled="disabled"/>
</div>
<!--! OVERVIEW SECTION. -->
@@ -45,7 +45,10 @@
local_fence = fence_data.isShared() != True;
nodes_using = cluster_data.getNodesUsingFence(entity_name)"
py:attrs="entity_name==name and {'class': 'chosen'} or (not i%2 and {'class': 'even'} or None)">
- <td class="checkbox"><input type="checkbox" name="${identifier}"/></td>
+ <td class="checkbox">
+ <input type="checkbox" name="${identifier}"
+ onclick="update_multi_action(this)" class="MultiActionItem"/>
+ </td>
<!--! If the fence is shared, display appropriate icon. -->
<td class="main_id"><a href="${tg.url(base_url + '/' + entity_name)}">${entity_name}</a></td>
<td class="fence_tlist_type">${fence_data.getPrettyName()}</td>
diff --git a/luci/templates/node.html b/luci/templates/node.html
index 050d1da..ff317d6 100644
--- a/luci/templates/node.html
+++ b/luci/templates/node.html
@@ -57,10 +57,10 @@
<xi:include href="submenu.html"/>
<div id="toolbar">
<a href="#" onclick="$('#add_nodes_dialog').dialog('open')" class="toolbar_button" id="tb_add">Add</a>
- <input type="submit" name="MultiAction" value="${_('Reboot')}" class="toolbar_button" id="tb_reboot" />
- <input type="submit" name="MultiAction" value="${_('Join Cluster')}" class="toolbar_button" id="tb_join" />
- <input type="submit" name="MultiAction" value="${_('Leave Cluster')}" class="toolbar_button" id="tb_leave" />
- <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button" id="tb_delete" />
+ <input type="submit" name="MultiAction" value="${_('Reboot')}" class="toolbar_button MultiAction" id="tb_reboot" disabled="disabled"/>
+ <input type="submit" name="MultiAction" value="${_('Join Cluster')}" class="toolbar_button MultiAction" id="tb_join" disabled="disabled"/>
+ <input type="submit" name="MultiAction" value="${_('Leave Cluster')}" class="toolbar_button MultiAction" id="tb_leave" disabled="disabled"/>
+ <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button MultiAction" id="tb_delete" disabled="disabled"/>
</div>
<!--! OVERVIEW SECTION. -->
@@ -89,7 +89,10 @@
nodeobj = cluster_data and cluster_data.getNodeByName(entity_name) or None;
is_clustered = node_data['clustered'];
nodedbobj = db_helpers.get_cluster_node(tmpl_context.cluster_name, entity_name);">
- <td class="checkbox"><input type="checkbox" name="${identifier}"/></td>
+ <td class="checkbox">
+ <input type="checkbox" name="${identifier}"
+ onclick="update_multi_action(this)" class="MultiActionItem"/>
+ </td>
<!--! Branch according to the status of the node. -->
<?python
if nodeobj == None:
@@ -163,7 +166,10 @@
</py:with>
<tr py:for="i in nodes" py:with="hostname = i.node_name">
<py:if test="cluster_status.nodes.keys().count(i.node_name) == 0">
- <td class="checkbox"><input type="checkbox" name="${hostname}"/></td>
+ <td class="checkbox">
+ <input type="checkbox" name="${hostname}"
+ onclick="update_multi_action(this)" class="MultiActionItem"/>
+ </td>
<td class="icon">
<img src="${tg.url('/images/question.png')}" alt="Status of the node is unknown." />
</td>
diff --git a/luci/templates/resource.html b/luci/templates/resource.html
index bae7c8d..68b4bb3 100644
--- a/luci/templates/resource.html
+++ b/luci/templates/resource.html
@@ -21,7 +21,7 @@
<xi:include href="submenu.html"/>
<div id="toolbar">
<a href="#" class="toolbar_button" id="tb_add" onclick="$('#add_resource_dialog').dialog('open')">Add</a>
- <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button" id="tb_delete" />
+ <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button MultiAction" id="tb_delete" disabled="disabled"/>
</div>
<!--! OVERVIEW SECTION. -->
@@ -44,6 +44,7 @@
py:attrs="entity_name==name and {'class': 'chosen'} or (not i%2 and {'class': 'even'} or None)">
<td class="checkbox">
<input type="checkbox" name="${entity_name}"
+ onclick="update_multi_action(this)" class="MultiActionItem"
py:attrs="refcount != 1 and {'disabled':'disabled'} or {}"/>
</td>
<td class="main_id">
diff --git a/luci/templates/service.html b/luci/templates/service.html
index 3a428d1..9bb6346 100644
--- a/luci/templates/service.html
+++ b/luci/templates/service.html
@@ -23,10 +23,10 @@
<xi:include href="submenu.html"/>
<div id="toolbar">
<a href="#" class="toolbar_button" id="tb_add" onclick="$('#add_service_dialog').dialog('open');return false;">Add</a>
- <input type="submit" name="MultiAction" value="${_('Start')}" class="toolbar_button" id="tb_start" />
- <input type="submit" name="MultiAction" value="${_('Restart')}" class="toolbar_button" id="tb_reboot" />
- <input type="submit" name="MultiAction" value="${_('Disable')}" class="toolbar_button" id="tb_disable" />
- <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button" id="tb_delete" />
+ <input type="submit" name="MultiAction" value="${_('Start')}" class="toolbar_button MultiAction" id="tb_start" disabled="disabled"/>
+ <input type="submit" name="MultiAction" value="${_('Restart')}" class="toolbar_button MultiAction" id="tb_reboot" disabled="disabled"/>
+ <input type="submit" name="MultiAction" value="${_('Disable')}" class="toolbar_button MultiAction" id="tb_disable" disabled="disabled"/>
+ <input type="submit" name="MultiAction" value="${_('Delete')}" class="toolbar_button MultiAction" id="tb_delete" disabled="disabled"/>
</div>
<!--! OVERVIEW SECTION. -->
@@ -50,7 +50,7 @@
entity_obj = (cluster_status and cluster_status.services and cluster_status.services.has_key(entity_name)) and cluster_status.services[entity_name] or {}"
py:attrs="entity_name==name and {'class': 'chosen'} or (not i%2 and {'class': 'even'} or None)">
<td class="checkbox">
- <input type="checkbox" name="${entity_name}"/>
+ <input type="checkbox" name="${entity_name}" onclick="update_multi_action(this)" class="MultiActionItem"/>
</td>
<!--! Branch according to the status of the service. -->
<py:choose test="hasattr(entity_obj, 'running') and entity_obj.running">
13 years, 3 months