|
 |
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 |
|