Blob Blame History Raw
From 1b6ed4d97198e7ca8c1fd5f76bfb8bfc95eeabdc Mon Sep 17 00:00:00 2001
From: Ivan Devat <idevat@redhat.com>
Date: Wed, 14 Sep 2016 09:37:06 +0200
Subject: [PATCH] squash bz1158500 add support for utilization attri

4ab84628f802 fix parsing of utilization attributes

18d526f59679 support utilization on (non-cib) remote node

f0b193a681e3 show error when show utilizat. on nonexistent node

9907c123c225 web UI: fix setting utilization attributes
---
 .pylintrc                     |  2 +-
 pcs/node.py                   | 54 ++++++++++++++++++++++++++++++++++++-----
 pcs/resource.py               |  8 ++++---
 pcs/test/test_node.py         | 56 +++++++++++++++++++++++++++++++++++++++++++
 pcs/test/test_resource.py     | 18 ++++++++++++++
 pcs/test/test_utils.py        | 17 +++++++++----
 pcs/utils.py                  | 12 +++++++++-
 pcsd/public/js/nodes-ember.js |  4 ++--
 pcsd/remote.rb                |  2 +-
 9 files changed, 155 insertions(+), 18 deletions(-)

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