Blame SOURCES/bz1158500-01-add-support-for-utilization-attributes.patch

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