|
|
15f218 |
From 1b6ed4d97198e7ca8c1fd5f76bfb8bfc95eeabdc Mon Sep 17 00:00:00 2001
|
|
|
15f218 |
From: Ivan Devat <idevat@redhat.com>
|
|
|
15f218 |
Date: Wed, 14 Sep 2016 09:37:06 +0200
|
|
|
15f218 |
Subject: [PATCH] squash bz1158500 add support for utilization attri
|
|
|
15f218 |
|
|
|
15f218 |
4ab84628f802 fix parsing of utilization attributes
|
|
|
15f218 |
|
|
|
15f218 |
18d526f59679 support utilization on (non-cib) remote node
|
|
|
15f218 |
|
|
|
15f218 |
f0b193a681e3 show error when show utilizat. on nonexistent node
|
|
|
15f218 |
|
|
|
15f218 |
9907c123c225 web UI: fix setting utilization attributes
|
|
|
15f218 |
---
|
|
|
15f218 |
.pylintrc | 2 +-
|
|
|
15f218 |
pcs/node.py | 54 ++++++++++++++++++++++++++++++++++++-----
|
|
|
15f218 |
pcs/resource.py | 8 ++++---
|
|
|
15f218 |
pcs/test/test_node.py | 56 +++++++++++++++++++++++++++++++++++++++++++
|
|
|
15f218 |
pcs/test/test_resource.py | 18 ++++++++++++++
|
|
|
15f218 |
pcs/test/test_utils.py | 17 +++++++++----
|
|
|
15f218 |
pcs/utils.py | 12 +++++++++-
|
|
|
15f218 |
pcsd/public/js/nodes-ember.js | 4 ++--
|
|
|
15f218 |
pcsd/remote.rb | 2 +-
|
|
|
15f218 |
9 files changed, 155 insertions(+), 18 deletions(-)
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/.pylintrc b/.pylintrc
|
|
|
15f218 |
index 1dd6d5d..6101381 100644
|
|
|
15f218 |
--- a/.pylintrc
|
|
|
15f218 |
+++ b/.pylintrc
|
|
|
15f218 |
@@ -92,7 +92,7 @@ dummy-variables-rgx=_$|dummy
|
|
|
15f218 |
|
|
|
15f218 |
[FORMAT]
|
|
|
15f218 |
# Maximum number of lines in a module
|
|
|
15f218 |
-max-module-lines=4584
|
|
|
15f218 |
+max-module-lines=4616
|
|
|
15f218 |
# Maximum number of characters on a single line.
|
|
|
15f218 |
max-line-length=1291
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/node.py b/pcs/node.py
|
|
|
15f218 |
index ed77d5d..729ea35 100644
|
|
|
15f218 |
--- a/pcs/node.py
|
|
|
15f218 |
+++ b/pcs/node.py
|
|
|
15f218 |
@@ -56,7 +56,10 @@ def node_cmd(argv):
|
|
|
15f218 |
elif len(argv) == 1:
|
|
|
15f218 |
print_node_utilization(argv.pop(0), filter_name=filter_name)
|
|
|
15f218 |
else:
|
|
|
15f218 |
- set_node_utilization(argv.pop(0), argv)
|
|
|
15f218 |
+ try:
|
|
|
15f218 |
+ set_node_utilization(argv.pop(0), argv)
|
|
|
15f218 |
+ except CmdLineInputError as e:
|
|
|
15f218 |
+ utils.exit_on_cmdline_input_errror(e, "node", "utilization")
|
|
|
15f218 |
# pcs-to-pcsd use only
|
|
|
15f218 |
elif sub_cmd == "pacemaker-status":
|
|
|
15f218 |
node_pacemaker_status()
|
|
|
15f218 |
@@ -150,17 +153,56 @@ def set_node_utilization(node, argv):
|
|
|
15f218 |
cib = utils.get_cib_dom()
|
|
|
15f218 |
node_el = utils.dom_get_node(cib, node)
|
|
|
15f218 |
if node_el is None:
|
|
|
15f218 |
- utils.err("Unable to find a node: {0}".format(node))
|
|
|
15f218 |
+ if utils.usefile:
|
|
|
15f218 |
+ utils.err("Unable to find a node: {0}".format(node))
|
|
|
15f218 |
|
|
|
15f218 |
- utils.dom_update_utilization(
|
|
|
15f218 |
- node_el, utils.convert_args_to_tuples(argv), "nodes-"
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ for attrs in utils.getNodeAttributesFromPacemaker():
|
|
|
15f218 |
+ if attrs.name == node and attrs.type == "remote":
|
|
|
15f218 |
+ node_attrs = attrs
|
|
|
15f218 |
+ break
|
|
|
15f218 |
+ else:
|
|
|
15f218 |
+ utils.err("Unable to find a node: {0}".format(node))
|
|
|
15f218 |
+
|
|
|
15f218 |
+ nodes_section_list = cib.getElementsByTagName("nodes")
|
|
|
15f218 |
+ if len(nodes_section_list) == 0:
|
|
|
15f218 |
+ utils.err("Unable to get nodes section of cib")
|
|
|
15f218 |
+
|
|
|
15f218 |
+ dom = nodes_section_list[0].ownerDocument
|
|
|
15f218 |
+ node_el = dom.createElement("node")
|
|
|
15f218 |
+ node_el.setAttribute("id", node_attrs.id)
|
|
|
15f218 |
+ node_el.setAttribute("type", node_attrs.type)
|
|
|
15f218 |
+ node_el.setAttribute("uname", node_attrs.name)
|
|
|
15f218 |
+ nodes_section_list[0].appendChild(node_el)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ utils.dom_update_utilization(node_el, prepare_options(argv), "nodes-")
|
|
|
15f218 |
utils.replace_cib_configuration(cib)
|
|
|
15f218 |
|
|
|
15f218 |
def print_node_utilization(filter_node=None, filter_name=None):
|
|
|
15f218 |
cib = utils.get_cib_dom()
|
|
|
15f218 |
+
|
|
|
15f218 |
+ node_element_list = cib.getElementsByTagName("node")
|
|
|
15f218 |
+
|
|
|
15f218 |
+
|
|
|
15f218 |
+ if(
|
|
|
15f218 |
+ filter_node
|
|
|
15f218 |
+ and
|
|
|
15f218 |
+ filter_node not in [
|
|
|
15f218 |
+ node_element.getAttribute("uname")
|
|
|
15f218 |
+ for node_element in node_element_list
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ and (
|
|
|
15f218 |
+ utils.usefile
|
|
|
15f218 |
+ or
|
|
|
15f218 |
+ filter_node not in [
|
|
|
15f218 |
+ node_attrs.name for node_attrs
|
|
|
15f218 |
+ in utils.getNodeAttributesFromPacemaker()
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ ):
|
|
|
15f218 |
+ utils.err("Unable to find a node: {0}".format(filter_node))
|
|
|
15f218 |
+
|
|
|
15f218 |
utilization = {}
|
|
|
15f218 |
- for node_el in cib.getElementsByTagName("node"):
|
|
|
15f218 |
+ for node_el in node_element_list:
|
|
|
15f218 |
node = node_el.getAttribute("uname")
|
|
|
15f218 |
if filter_node is not None and node != filter_node:
|
|
|
15f218 |
continue
|
|
|
15f218 |
diff --git a/pcs/resource.py b/pcs/resource.py
|
|
|
15f218 |
index 74adac6..046a826 100644
|
|
|
15f218 |
--- a/pcs/resource.py
|
|
|
15f218 |
+++ b/pcs/resource.py
|
|
|
15f218 |
@@ -191,7 +191,10 @@ def resource_cmd(argv):
|
|
|
15f218 |
elif len(argv) == 1:
|
|
|
15f218 |
print_resource_utilization(argv.pop(0))
|
|
|
15f218 |
else:
|
|
|
15f218 |
- set_resource_utilization(argv.pop(0), argv)
|
|
|
15f218 |
+ try:
|
|
|
15f218 |
+ set_resource_utilization(argv.pop(0), argv)
|
|
|
15f218 |
+ except CmdLineInputError as e:
|
|
|
15f218 |
+ utils.exit_on_cmdline_input_errror(e, "resource", "utilization")
|
|
|
15f218 |
elif (sub_cmd == "get_resource_agent_info"):
|
|
|
15f218 |
get_resource_agent_info(argv)
|
|
|
15f218 |
else:
|
|
|
15f218 |
@@ -2795,8 +2798,7 @@ def set_resource_utilization(resource_id, argv):
|
|
|
15f218 |
resource_el = utils.dom_get_resource(cib, resource_id)
|
|
|
15f218 |
if resource_el is None:
|
|
|
15f218 |
utils.err("Unable to find a resource: {0}".format(resource_id))
|
|
|
15f218 |
-
|
|
|
15f218 |
- utils.dom_update_utilization(resource_el, utils.convert_args_to_tuples(argv))
|
|
|
15f218 |
+ utils.dom_update_utilization(resource_el, prepare_options(argv))
|
|
|
15f218 |
utils.replace_cib_configuration(cib)
|
|
|
15f218 |
|
|
|
15f218 |
def print_resource_utilization(resource_id):
|
|
|
15f218 |
diff --git a/pcs/test/test_node.py b/pcs/test/test_node.py
|
|
|
15f218 |
index 9b45e07..137c7c7 100644
|
|
|
15f218 |
--- a/pcs/test/test_node.py
|
|
|
15f218 |
+++ b/pcs/test/test_node.py
|
|
|
15f218 |
@@ -7,7 +7,9 @@ from __future__ import (
|
|
|
15f218 |
|
|
|
15f218 |
import shutil
|
|
|
15f218 |
from pcs.test.tools import pcs_unittest as unittest
|
|
|
15f218 |
+from pcs.test.tools.pcs_unittest import mock
|
|
|
15f218 |
|
|
|
15f218 |
+from pcs import node
|
|
|
15f218 |
from pcs.test.tools.assertions import AssertPcsMixin
|
|
|
15f218 |
from pcs.test.tools.misc import (
|
|
|
15f218 |
ac,
|
|
|
15f218 |
@@ -268,6 +270,20 @@ Node Utilization:
|
|
|
15f218 |
self.assertEqual(0, returnVal)
|
|
|
15f218 |
|
|
|
15f218 |
def test_node_utilization_set_invalid(self):
|
|
|
15f218 |
+ output, returnVal = pcs(temp_cib, "node utilization rh7-1 test")
|
|
|
15f218 |
+ expected_out = """\
|
|
|
15f218 |
+Error: missing value of 'test' option
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ ac(expected_out, output)
|
|
|
15f218 |
+ self.assertEqual(1, returnVal)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ output, returnVal = pcs(temp_cib, "node utilization rh7-1 =10")
|
|
|
15f218 |
+ expected_out = """\
|
|
|
15f218 |
+Error: missing key in '=10' option
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ ac(expected_out, output)
|
|
|
15f218 |
+ self.assertEqual(1, returnVal)
|
|
|
15f218 |
+
|
|
|
15f218 |
output, returnVal = pcs(temp_cib, "node utilization rh7-0 test=10")
|
|
|
15f218 |
expected_out = """\
|
|
|
15f218 |
Error: Unable to find a node: rh7-0
|
|
|
15f218 |
@@ -524,3 +540,43 @@ Node Attributes:
|
|
|
15f218 |
"node attribute rh7-1 missing= --force",
|
|
|
15f218 |
""
|
|
|
15f218 |
)
|
|
|
15f218 |
+
|
|
|
15f218 |
+class SetNodeUtilizationTest(unittest.TestCase, AssertPcsMixin):
|
|
|
15f218 |
+ def setUp(self):
|
|
|
15f218 |
+ shutil.copy(empty_cib, temp_cib)
|
|
|
15f218 |
+ self.pcs_runner = PcsRunner(temp_cib)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_refuse_non_option_attribute_parameter_among_options(self):
|
|
|
15f218 |
+ self.assert_pcs_fail("node utilization rh7-1 net", [
|
|
|
15f218 |
+ "Error: missing value of 'net' option",
|
|
|
15f218 |
+ ])
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_refuse_option_without_key(self):
|
|
|
15f218 |
+ self.assert_pcs_fail("node utilization rh7-1 =1", [
|
|
|
15f218 |
+ "Error: missing key in '=1' option",
|
|
|
15f218 |
+ ])
|
|
|
15f218 |
+
|
|
|
15f218 |
+class PrintNodeUtilizationTest(unittest.TestCase, AssertPcsMixin):
|
|
|
15f218 |
+ def setUp(self):
|
|
|
15f218 |
+ shutil.copy(empty_cib, temp_cib)
|
|
|
15f218 |
+ self.pcs_runner = PcsRunner(temp_cib)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ @mock.patch("pcs.node.utils")
|
|
|
15f218 |
+ def test_refuse_when_node_not_in_cib_and_is_not_remote(self, mock_utils):
|
|
|
15f218 |
+ mock_cib = mock.MagicMock()
|
|
|
15f218 |
+ mock_cib.getElementsByTagName = mock.Mock(return_value=[])
|
|
|
15f218 |
+
|
|
|
15f218 |
+ mock_utils.get_cib_dom = mock.Mock(return_value=mock_cib)
|
|
|
15f218 |
+ mock_utils.usefile = False
|
|
|
15f218 |
+ mock_utils.getNodeAttributesFromPacemaker = mock.Mock(return_value=[])
|
|
|
15f218 |
+ mock_utils.err = mock.Mock(side_effect=SystemExit)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ self.assertRaises(
|
|
|
15f218 |
+ SystemExit,
|
|
|
15f218 |
+ lambda: node.print_node_utilization("some")
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_refuse_when_node_not_in_mocked_cib(self):
|
|
|
15f218 |
+ self.assert_pcs_fail("node utilization some_nonexistent_node", [
|
|
|
15f218 |
+ "Error: Unable to find a node: some_nonexistent_node",
|
|
|
15f218 |
+ ])
|
|
|
15f218 |
diff --git a/pcs/test/test_resource.py b/pcs/test/test_resource.py
|
|
|
15f218 |
index 87a7fa8..d32cfb4 100644
|
|
|
15f218 |
--- a/pcs/test/test_resource.py
|
|
|
15f218 |
+++ b/pcs/test/test_resource.py
|
|
|
15f218 |
@@ -4430,6 +4430,24 @@ Resource Utilization:
|
|
|
15f218 |
self.assertEqual(0, returnVal)
|
|
|
15f218 |
|
|
|
15f218 |
def test_resource_utilization_set_invalid(self):
|
|
|
15f218 |
+ output, returnVal = pcs(
|
|
|
15f218 |
+ temp_large_cib, "resource utilization dummy test"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ expected_out = """\
|
|
|
15f218 |
+Error: missing value of 'test' option
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ ac(expected_out, output)
|
|
|
15f218 |
+ self.assertEqual(1, returnVal)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ output, returnVal = pcs(
|
|
|
15f218 |
+ temp_large_cib, "resource utilization dummy =10"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ expected_out = """\
|
|
|
15f218 |
+Error: missing key in '=10' option
|
|
|
15f218 |
+"""
|
|
|
15f218 |
+ ac(expected_out, output)
|
|
|
15f218 |
+ self.assertEqual(1, returnVal)
|
|
|
15f218 |
+
|
|
|
15f218 |
output, returnVal = pcs(temp_large_cib, "resource utilization dummy0")
|
|
|
15f218 |
expected_out = """\
|
|
|
15f218 |
Error: Unable to find a resource: dummy0
|
|
|
15f218 |
diff --git a/pcs/test/test_utils.py b/pcs/test/test_utils.py
|
|
|
15f218 |
index 252de30..c4c6d87 100644
|
|
|
15f218 |
--- a/pcs/test/test_utils.py
|
|
|
15f218 |
+++ b/pcs/test/test_utils.py
|
|
|
15f218 |
@@ -1400,12 +1400,12 @@ class UtilsTest(unittest.TestCase):
|
|
|
15f218 |
""").documentElement
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
SystemExit,
|
|
|
15f218 |
- utils.dom_update_utilization, el, [("name", "invalid_val")]
|
|
|
15f218 |
+ utils.dom_update_utilization, el, {"name": "invalid_val"}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
self.assertRaises(
|
|
|
15f218 |
SystemExit,
|
|
|
15f218 |
- utils.dom_update_utilization, el, [("name", "0.01")]
|
|
|
15f218 |
+ utils.dom_update_utilization, el, {"name": "0.01"}
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
sys.stderr = tmp_stderr
|
|
|
15f218 |
@@ -1415,7 +1415,12 @@ class UtilsTest(unittest.TestCase):
|
|
|
15f218 |
<resource id="test_id"/>
|
|
|
15f218 |
""").documentElement
|
|
|
15f218 |
utils.dom_update_utilization(
|
|
|
15f218 |
- el, [("name", ""), ("key", "-1"), ("keys", "90")]
|
|
|
15f218 |
+ el,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "name": "",
|
|
|
15f218 |
+ "key": "-1",
|
|
|
15f218 |
+ "keys": "90",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
self.assertEqual(len(dom_get_child_elements(el)), 1)
|
|
|
15f218 |
@@ -1459,7 +1464,11 @@ class UtilsTest(unittest.TestCase):
|
|
|
15f218 |
</resource>
|
|
|
15f218 |
""").documentElement
|
|
|
15f218 |
utils.dom_update_utilization(
|
|
|
15f218 |
- el, [("key", "100"), ("keys", "")]
|
|
|
15f218 |
+ el,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "key": "100",
|
|
|
15f218 |
+ "keys": "",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
u = dom_get_child_elements(el)[0]
|
|
|
15f218 |
diff --git a/pcs/utils.py b/pcs/utils.py
|
|
|
15f218 |
index a7ff7ca..d5b6dcf 100644
|
|
|
15f218 |
--- a/pcs/utils.py
|
|
|
15f218 |
+++ b/pcs/utils.py
|
|
|
15f218 |
@@ -472,6 +472,16 @@ def getNodesFromPacemaker():
|
|
|
15f218 |
except LibraryError as e:
|
|
|
15f218 |
process_library_reports(e.args)
|
|
|
15f218 |
|
|
|
15f218 |
+def getNodeAttributesFromPacemaker():
|
|
|
15f218 |
+ try:
|
|
|
15f218 |
+ return [
|
|
|
15f218 |
+ node.attrs
|
|
|
15f218 |
+ for node in ClusterState(getClusterStateXml()).node_section.nodes
|
|
|
15f218 |
+ ]
|
|
|
15f218 |
+ except LibraryError as e:
|
|
|
15f218 |
+ process_library_reports(e.args)
|
|
|
15f218 |
+
|
|
|
15f218 |
+
|
|
|
15f218 |
def hasCorosyncConf(conf=None):
|
|
|
15f218 |
if not conf:
|
|
|
15f218 |
if is_rhel6():
|
|
|
15f218 |
@@ -2487,7 +2497,7 @@ def dom_update_utilization(dom_element, attributes, id_prefix=""):
|
|
|
15f218 |
id_prefix + dom_element.getAttribute("id") + "-utilization"
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
- for name, value in attributes:
|
|
|
15f218 |
+ for name, value in sorted(attributes.items()):
|
|
|
15f218 |
if value != "" and not is_int(value):
|
|
|
15f218 |
err(
|
|
|
15f218 |
"Value of utilization attribute must be integer: "
|
|
|
15f218 |
diff --git a/pcsd/public/js/nodes-ember.js b/pcsd/public/js/nodes-ember.js
|
|
|
15f218 |
index c650fe6..19caf14 100644
|
|
|
15f218 |
--- a/pcsd/public/js/nodes-ember.js
|
|
|
15f218 |
+++ b/pcsd/public/js/nodes-ember.js
|
|
|
15f218 |
@@ -500,9 +500,9 @@ Pcs.UtilizationTableComponent = Ember.Component.extend({
|
|
|
15f218 |
},
|
|
|
15f218 |
add: function(form_id) {
|
|
|
15f218 |
var id = "#" + form_id;
|
|
|
15f218 |
- var name = $(id + " input[name='new_utilization_name']").val();
|
|
|
15f218 |
+ var name = $(id + " input[name='new_utilization_name']").val().trim();
|
|
|
15f218 |
if (name == "") {
|
|
|
15f218 |
- return;
|
|
|
15f218 |
+ alert("Name of utilization attribute should be non-empty string.");
|
|
|
15f218 |
}
|
|
|
15f218 |
var value = $(id + " input[name='new_utilization_value']").val().trim();
|
|
|
15f218 |
if (!is_integer(value)) {
|
|
|
15f218 |
diff --git a/pcsd/remote.rb b/pcsd/remote.rb
|
|
|
15f218 |
index e467d0a..7dc7951 100644
|
|
|
15f218 |
--- a/pcsd/remote.rb
|
|
|
15f218 |
+++ b/pcsd/remote.rb
|
|
|
15f218 |
@@ -2240,7 +2240,7 @@ def set_node_utilization(params, reqest, auth_user)
|
|
|
15f218 |
|
|
|
15f218 |
if retval != 0
|
|
|
15f218 |
return [400, "Unable to set utilization '#{name}=#{value}' for node " +
|
|
|
15f218 |
- "'#{res_id}': #{stderr.join('')}"
|
|
|
15f218 |
+ "'#{node}': #{stderr.join('')}"
|
|
|
15f218 |
]
|
|
|
15f218 |
end
|
|
|
15f218 |
return 200
|
|
|
15f218 |
--
|
|
|
15f218 |
1.8.3.1
|
|
|
15f218 |
|