Blame SOURCES/bz1305049-02-pcs-does-not-support-ticket-constraints.patch

15f218
From deac91b1fc74065d01342420accfd1af88237237 Mon Sep 17 00:00:00 2001
15f218
From: Ivan Devat <idevat@redhat.com>
15f218
Date: Tue, 20 Sep 2016 08:20:29 +0200
15f218
Subject: [PATCH] squash bz1305049 pcs does not support "ticket" con
15f218
15f218
07ae6704fff5 fail when no matching ticket constraint for remove
15f218
15f218
d25ed3d9bc65 fix help for constraint ticket commands
15f218
15f218
66b91ba0da7e fix manpage for booth ticket add command
15f218
15f218
2710bc2e15c2 fix manpage for constraint ticket set
15f218
---
15f218
 pcs/cli/constraint_ticket/command.py       |  4 ++-
15f218
 pcs/lib/cib/constraint/ticket.py           |  4 +++
15f218
 pcs/lib/cib/test/test_constraint_ticket.py | 53 +++++++++++++++++++++++++++---
15f218
 pcs/lib/commands/constraint/ticket.py      | 15 +++++++--
15f218
 pcs/lib/commands/test/test_ticket.py       | 20 +++++++++++
15f218
 pcs/pcs.8                                  |  6 ++--
15f218
 pcs/test/test_constraints.py               |  8 +++++
15f218
 pcs/usage.py                               | 10 +++---
15f218
 8 files changed, 105 insertions(+), 15 deletions(-)
15f218
15f218
diff --git a/pcs/cli/constraint_ticket/command.py b/pcs/cli/constraint_ticket/command.py
15f218
index 0ed4fdd..583ba9e 100644
15f218
--- a/pcs/cli/constraint_ticket/command.py
15f218
+++ b/pcs/cli/constraint_ticket/command.py
15f218
@@ -8,6 +8,7 @@ from __future__ import (
15f218
 from pcs.cli.common.errors import CmdLineInputError
15f218
 from pcs.cli.constraint import command
15f218
 from pcs.cli.constraint_ticket import parse_args, console_report
15f218
+from pcs.cli.common.console_report import error
15f218
 
15f218
 def create_with_set(lib, argv, modificators):
15f218
     """
15f218
@@ -56,7 +57,8 @@ def remove(lib, argv, modificators):
15f218
     if len(argv) != 2:
15f218
         raise CmdLineInputError()
15f218
     ticket, resource_id = argv
15f218
-    lib.constraint_ticket.remove(ticket, resource_id)
15f218
+    if not lib.constraint_ticket.remove(ticket, resource_id):
15f218
+        raise error("no matching ticket constraint found")
15f218
 
15f218
 def show(lib, argv, modificators):
15f218
     """
15f218
diff --git a/pcs/lib/cib/constraint/ticket.py b/pcs/lib/cib/constraint/ticket.py
15f218
index c708794..85d045c 100644
15f218
--- a/pcs/lib/cib/constraint/ticket.py
15f218
+++ b/pcs/lib/cib/constraint/ticket.py
15f218
@@ -113,6 +113,8 @@ def remove_plain(constraint_section, ticket_key, resource_id):
15f218
     for ticket_element in ticket_element_list:
15f218
         ticket_element.getparent().remove(ticket_element)
15f218
 
15f218
+    return len(ticket_element_list) > 0
15f218
+
15f218
 def remove_with_resource_set(constraint_section, ticket_key, resource_id):
15f218
     ref_element_list = constraint_section.xpath(
15f218
         './/rsc_ticket[@ticket="{0}"]/resource_set/resource_ref[@id="{1}"]'
15f218
@@ -128,6 +130,8 @@ def remove_with_resource_set(constraint_section, ticket_key, resource_id):
15f218
             if not len(ticket_element):
15f218
                 ticket_element.getparent().remove(ticket_element)
15f218
 
15f218
+    return len(ref_element_list) > 0
15f218
+
15f218
 def are_duplicate_plain(element, other_element):
15f218
     return all(
15f218
         element.attrib.get(name, "") == other_element.attrib.get(name, "")
15f218
diff --git a/pcs/lib/cib/test/test_constraint_ticket.py b/pcs/lib/cib/test/test_constraint_ticket.py
15f218
index d3da004..b720b55 100644
15f218
--- a/pcs/lib/cib/test/test_constraint_ticket.py
15f218
+++ b/pcs/lib/cib/test/test_constraint_ticket.py
15f218
@@ -324,11 +324,34 @@ class RemovePlainTest(TestCase):
15f218
             </constraints>
15f218
         """)
15f218
 
15f218
-        ticket.remove_plain(
15f218
+        self.assertTrue(ticket.remove_plain(
15f218
             constraint_section,
15f218
             ticket_key="tA",
15f218
             resource_id="rA",
15f218
-        )
15f218
+        ))
15f218
+
15f218
+        assert_xml_equal(etree.tostring(constraint_section).decode(), """
15f218
+            <constraints>
15f218
+                <rsc_ticket id="t2" ticket="tA" rsc="rB"/>
15f218
+                <rsc_ticket id="t4" ticket="tB" rsc="rA"/>
15f218
+                <rsc_ticket id="t5" ticket="tB" rsc="rB"/>
15f218
+            </constraints>
15f218
+        """)
15f218
+
15f218
+    def test_remove_nothing_when_no_matching_found(self):
15f218
+        constraint_section = etree.fromstring("""
15f218
+            <constraints>
15f218
+                <rsc_ticket id="t2" ticket="tA" rsc="rB"/>
15f218
+                <rsc_ticket id="t4" ticket="tB" rsc="rA"/>
15f218
+                <rsc_ticket id="t5" ticket="tB" rsc="rB"/>
15f218
+            </constraints>
15f218
+        """)
15f218
+
15f218
+        self.assertFalse(ticket.remove_plain(
15f218
+            constraint_section,
15f218
+            ticket_key="tA",
15f218
+            resource_id="rA",
15f218
+        ))
15f218
 
15f218
         assert_xml_equal(etree.tostring(constraint_section).decode(), """
15f218
             <constraints>
15f218
@@ -369,11 +392,11 @@ class RemoveWithSetTest(TestCase):
15f218
             </constraints>
15f218
         """)
15f218
 
15f218
-        ticket.remove_with_resource_set(
15f218
+        self.assertTrue(ticket.remove_with_resource_set(
15f218
             constraint_section,
15f218
             ticket_key="tA",
15f218
             resource_id="rA"
15f218
-        )
15f218
+        ))
15f218
 
15f218
         assert_xml_equal(
15f218
             """
15f218
@@ -393,3 +416,25 @@ class RemoveWithSetTest(TestCase):
15f218
             """,
15f218
             etree.tostring(constraint_section).decode()
15f218
         )
15f218
+
15f218
+    def test_remove_nothing_when_no_matching_found(self):
15f218
+        constraint_section = etree.fromstring("""
15f218
+                <constraints>
15f218
+                    <rsc_ticket id="t2" ticket="tA">
15f218
+                        <resource_set id="rs3">
15f218
+                            <resource_ref id="rB"/>
15f218
+                        </resource_set>
15f218
+                    </rsc_ticket>
15f218
+
15f218
+                    <rsc_ticket id="t3" ticket="tB">
15f218
+                        <resource_set id="rs5">
15f218
+                            <resource_ref id="rA"/>
15f218
+                        </resource_set>
15f218
+                    </rsc_ticket>
15f218
+                </constraints>
15f218
+        """)
15f218
+        self.assertFalse(ticket.remove_with_resource_set(
15f218
+            constraint_section,
15f218
+            ticket_key="tA",
15f218
+            resource_id="rA"
15f218
+        ))
15f218
diff --git a/pcs/lib/commands/constraint/ticket.py b/pcs/lib/commands/constraint/ticket.py
15f218
index 2ea7afc..a14c5ad 100644
15f218
--- a/pcs/lib/commands/constraint/ticket.py
15f218
+++ b/pcs/lib/commands/constraint/ticket.py
15f218
@@ -77,6 +77,17 @@ def remove(env, ticket_key, resource_id):
15f218
     """
15f218
     cib = env.get_cib()
15f218
     constraint_section = get_constraints(cib)
15f218
-    ticket.remove_plain(constraint_section, ticket_key, resource_id)
15f218
-    ticket.remove_with_resource_set(constraint_section, ticket_key, resource_id)
15f218
+    any_plain_removed = ticket.remove_plain(
15f218
+        constraint_section,
15f218
+        ticket_key,
15f218
+        resource_id
15f218
+    )
15f218
+    any_with_resource_set_removed = ticket.remove_with_resource_set(
15f218
+        constraint_section,
15f218
+        ticket_key,
15f218
+        resource_id
15f218
+    )
15f218
+
15f218
     env.push_cib(cib)
15f218
+
15f218
+    return any_plain_removed or any_with_resource_set_removed
15f218
diff --git a/pcs/lib/commands/test/test_ticket.py b/pcs/lib/commands/test/test_ticket.py
15f218
index 586ca4b..edf592a 100644
15f218
--- a/pcs/lib/commands/test/test_ticket.py
15f218
+++ b/pcs/lib/commands/test/test_ticket.py
15f218
@@ -6,6 +6,8 @@ from __future__ import (
15f218
 )
15f218
 
15f218
 from pcs.test.tools.pcs_unittest import TestCase
15f218
+from pcs.test.tools.pcs_unittest import mock
15f218
+from pcs.test.tools.misc import create_patcher
15f218
 
15f218
 from pcs.common import report_codes
15f218
 from pcs.lib.commands.constraint import ticket as ticket_command
15f218
@@ -18,6 +20,7 @@ from pcs.test.tools.assertions import (
15f218
 from pcs.test.tools.misc import get_test_resource as rc
15f218
 from pcs.test.tools.xml import get_xml_manipulation_creator_from_file
15f218
 
15f218
+patch_commands = create_patcher("pcs.lib.commands.constraint.ticket")
15f218
 
15f218
 class CreateTest(TestCase):
15f218
     def setUp(self):
15f218
@@ -65,3 +68,20 @@ class CreateTest(TestCase):
15f218
                 {"resource_id": "resourceA"},
15f218
             ),
15f218
         )
15f218
+
15f218
+@patch_commands("get_constraints", mock.Mock)
15f218
+class RemoveTest(TestCase):
15f218
+    @patch_commands("ticket.remove_plain", mock.Mock(return_value=1))
15f218
+    @patch_commands("ticket.remove_with_resource_set",mock.Mock(return_value=0))
15f218
+    def test_successfully_remove_plain(self):
15f218
+        self.assertTrue(ticket_command.remove(mock.MagicMock(), "T", "R"))
15f218
+
15f218
+    @patch_commands("ticket.remove_plain", mock.Mock(return_value=0))
15f218
+    @patch_commands("ticket.remove_with_resource_set",mock.Mock(return_value=1))
15f218
+    def test_successfully_remove_with_resource_set(self):
15f218
+        self.assertTrue(ticket_command.remove(mock.MagicMock(), "T", "R"))
15f218
+
15f218
+    @patch_commands("ticket.remove_plain", mock.Mock(return_value=0))
15f218
+    @patch_commands("ticket.remove_with_resource_set",mock.Mock(return_value=0))
15f218
+    def test_raises_library_error_when_no_matching_constraint_found(self):
15f218
+        self.assertFalse(ticket_command.remove(mock.MagicMock(), "T", "R"))
15f218
diff --git a/pcs/pcs.8 b/pcs/pcs.8
15f218
index 40b146f..1efe8f4 100644
15f218
--- a/pcs/pcs.8
15f218
+++ b/pcs/pcs.8
15f218
@@ -484,10 +484,10 @@ Remove colocation constraints with specified resources.
15f218
 ticket [show] [\fB\-\-full\fR]
15f218
 List all current ticket constraints (if \fB\-\-full\fR is specified show the internal constraint id's as well).
15f218
 .TP
15f218
-ticket add <ticket> [<role>] <resource id> [options] [id=constraint\-id]
15f218
+ticket add <ticket> [<role>] <resource id> [<options>] [id=<constraint\-id>]
15f218
 Create a ticket constraint for <resource id>. Available option is loss-policy=fence/stop/freeze/demote. A role can be master, slave, started or stopped.
15f218
 .TP
15f218
-ticket set <resource1> [resourceN]... [options] [set <resourceX> ... [options]] [setoptions [constraint_options]]
15f218
+ticket set <resource1> [<resourceN>]... [<options>] [set <resourceX> ... [<options>]] setoptions <constraint_options>
15f218
 Create a ticket constraint with a resource set. Available options are sequential=true/false, require-all=true/false, action=start/promote/demote/stop and role=Stopped/Started/Master/Slave. Required constraint option is ticket=<ticket>. Optional constraint options are id=<constraint-id> and loss-policy=fence/stop/freeze/demote.
15f218
 .TP
15f218
 ticket remove <ticket> <resource id>
15f218
@@ -587,7 +587,7 @@ Write new booth configuration with specified sites and arbitrators.  Total numbe
15f218
 destroy
15f218
 Remove booth configuration files.
15f218
 .TP
15f218
-ticket add <ticket>
15f218
+ticket add <ticket> [<name>=<value> ...]
15f218
 Add new ticket to the current configuration. Ticket options are specified in booth manpage.
15f218
 
15f218
 .TP
15f218
diff --git a/pcs/test/test_constraints.py b/pcs/test/test_constraints.py
15f218
index 4007e90..fee7093 100644
15f218
--- a/pcs/test/test_constraints.py
15f218
+++ b/pcs/test/test_constraints.py
15f218
@@ -2721,6 +2721,14 @@ class TicketRemoveTest(ConstraintBaseTest):
15f218
             "    set B setoptions ticket=T",
15f218
         ])
15f218
 
15f218
+    def test_fail_when_no_matching_ticket_constraint_here(self):
15f218
+        self.assert_pcs_success("constraint ticket show", stdout_full=[
15f218
+            "Ticket Constraints:",
15f218
+        ])
15f218
+        self.assert_pcs_fail("constraint ticket remove T A", [
15f218
+            "Error: no matching ticket constraint found"
15f218
+        ])
15f218
+
15f218
 
15f218
 class TicketShow(ConstraintBaseTest):
15f218
     def test_show_set(self):
15f218
diff --git a/pcs/usage.py b/pcs/usage.py
15f218
index 764e3fc..ea407c3 100644
15f218
--- a/pcs/usage.py
15f218
+++ b/pcs/usage.py
15f218
@@ -996,15 +996,15 @@ Commands:
15f218
         List all current ticket constraints (if --full is specified show
15f218
         the internal constraint id's as well).
15f218
 
15f218
-    ticket add <ticket> [<role>] <resource id> [options]
15f218
-               [id=constraint-id]
15f218
+    ticket add <ticket> [<role>] <resource id> [<options>]
15f218
+               [id=<constraint-id>]
15f218
         Create a ticket constraint for <resource id>.
15f218
         Available option is loss-policy=fence/stop/freeze/demote.
15f218
         A role can be master, slave, started or stopped.
15f218
 
15f218
-    ticket set <resource1> [resourceN]... [options]
15f218
-               [set <resourceX> ... [options]]
15f218
-               [setoptions [constraint_options]]
15f218
+    ticket set <resource1> [<resourceN>]... [<options>]
15f218
+               [set <resourceX> ... [<options>]]
15f218
+               setoptions <constraint_options>
15f218
         Create a ticket constraint with a resource set.
15f218
         Available options are sequential=true/false, require-all=true/false,
15f218
         action=start/promote/demote/stop and role=Stopped/Started/Master/Slave.
15f218
-- 
15f218
1.8.3.1
15f218