1    	# pylint: disable=too-many-lines
2    	import xml.dom.minidom
3    	from textwrap import dedent
4    	from unittest import (
5    	    TestCase,
6    	    mock,
7    	)
8    	
9    	from pcs import (
10   	    rule,
11   	    utils,
12   	)
13   	from pcs.common import const
14   	from pcs.common.str_tools import format_list_custom_last_separator
15   	
16   	from pcs_test.tools.assertions import (
17   	    AssertPcsMixin,
18   	    ac,
19   	    assert_xml_equal,
20   	)
21   	from pcs_test.tools.bin_mock import get_mock_settings
22   	from pcs_test.tools.misc import get_test_resource as rc
23   	from pcs_test.tools.misc import (
24   	    get_tmp_file,
25   	    skip_unless_crm_rule,
26   	    write_file_to_tmpfile,
27   	)
28   	from pcs_test.tools.pcs_runner import PcsRunner
29   	
30   	# pylint: disable=invalid-name
31   	
32   	empty_cib = rc("cib-empty-3.2.xml")
33   	empty_cib_new_roles_supported = rc("cib-empty-3.7.xml")
34   	
35   	
36   	class DateValueTest(TestCase):
37   	    def testParse(self):
38   	        for value, item in enumerate(rule.DateCommonValue.allowed_items, 1):
39   	            self.assertEqual(
40   	                str(value),
41   	                rule.DateCommonValue("%s=%s" % (item, value)).parts[item],
42   	            )
43   	
44   	        value = rule.DateCommonValue(
45   	            "hours=1 monthdays=2 weekdays=3 yeardays=4 months=5 weeks=6 "
46   	            "years=7 weekyears=8 moon=9"
47   	        )
48   	        self.assertEqual("1", value.parts["hours"])
49   	        self.assertEqual("2", value.parts["monthdays"])
50   	        self.assertEqual("3", value.parts["weekdays"])
51   	        self.assertEqual("4", value.parts["yeardays"])
52   	        self.assertEqual("5", value.parts["months"])
53   	        self.assertEqual("6", value.parts["weeks"])
54   	        self.assertEqual("7", value.parts["years"])
55   	        self.assertEqual("8", value.parts["weekyears"])
56   	        self.assertEqual("9", value.parts["moon"])
57   	
58   	        value = rule.DateCommonValue("hours=1 monthdays=2 hours=3")
59   	        self.assertEqual("2", value.parts["monthdays"])
60   	        self.assertEqual("3", value.parts["hours"])
61   	
62   	        value = rule.DateCommonValue(" hours=1   monthdays=2   hours=3 ")
63   	        self.assertEqual("2", value.parts["monthdays"])
64   	        self.assertEqual("3", value.parts["hours"])
65   	
66   	        self.assertSyntaxError(
67   	            "missing one of 'hours=', 'monthdays=', 'weekdays=', 'yeardays=', "
68   	            "'months=', 'weeks=', 'years=', 'weekyears=', 'moon=' in date-spec",
69   	            "",
70   	            rule.DateSpecValue,
71   	        )
72   	        self.assertSyntaxError(
73   	            "missing value after 'hours=' in date-spec",
74   	            "hours=",
75   	            rule.DateSpecValue,
76   	        )
77   	        self.assertSyntaxError(
78   	            "missing =value after 'hours' in date-spec",
79   	            "hours",
80   	            rule.DateSpecValue,
81   	        )
82   	        self.assertSyntaxError(
83   	            "unexpected 'foo=bar' in date-spec", "foo=bar", rule.DateSpecValue
84   	        )
85   	        self.assertSyntaxError(
86   	            "unexpected 'foo=bar' in date-spec",
87   	            "hours=1 foo=bar",
88   	            rule.DateSpecValue,
89   	        )
90   	
91   	    # already moved to the new architecture tests
92   	    @mock.patch("pcs.rule.deprecation_warning")
93   	    def testDurationValidate(self, mock_deprecation):
94   	        for value, item in enumerate(rule.DateCommonValue.allowed_items, 1):
95   	            self.assertEqual(
96   	                str(value),
97   	                rule.DateDurationValue("%s=%s" % (item, value)).parts[item],
98   	            )
99   	        deprecation_call_list = [
100  	            mock.call(
101  	                f"duration option '{option}' is deprecated and might be removed "
102  	                "in a future release, therefore it should not be used"
103  	            )
104  	            for option in ("monthdays", "weekdays", "weekyears", "moon")
105  	        ]
106  	        mock_deprecation.assert_has_calls(deprecation_call_list)
107  	        self.assertEqual(
108  	            mock_deprecation.call_count, len(deprecation_call_list)
109  	        )
110  	        for item in rule.DateCommonValue.allowed_items:
111  	            self.assertSyntaxError(
112  	                "invalid %s '%s' in 'duration'" % (item, "foo"),
113  	                "%s=foo" % item,
114  	                rule.DateDurationValue,
115  	            )
116  	            self.assertSyntaxError(
117  	                "invalid %s '%s' in 'duration'" % (item, "-1"),
118  	                "%s=-1" % item,
119  	                rule.DateDurationValue,
120  	            )
121  	            self.assertSyntaxError(
122  	                "invalid %s '%s' in 'duration'" % (item, "2foo"),
123  	                "%s=2foo" % item,
124  	                rule.DateDurationValue,
125  	            )
126  	
127  	    # already moved to the new architecture tests
128  	    @mock.patch("pcs.rule.deprecation_warning")
129  	    def testDateSpecValidation(self, mock_deprecation):
130  	        for item in rule.DateCommonValue.allowed_items:
131  	            value = 1
132  	            self.assertEqual(
133  	                str(value),
134  	                rule.DateSpecValue("%s=%s" % (item, value)).parts[item],
135  	            )
136  	            self.assertEqual(
137  	                "%s-%s" % (value, value + 1),
138  	                rule.DateSpecValue("%s=%s-%s" % (item, value, value + 1)).parts[
139  	                    item
140  	                ],
141  	            )
142  	        deprecation_call_list = [
143  	            mock.call(
144  	                f"date-spec option '{option}' is deprecated and might be removed "
145  	                "in a future release, therefore it should not be used"
146  	            )
147  	            for option in ("moon", "moon")
148  	        ]
149  	        mock_deprecation.assert_has_calls(deprecation_call_list)
150  	        self.assertEqual(
151  	            mock_deprecation.call_count, len(deprecation_call_list)
152  	        )
153  	        self.assertEqual(
154  	            "hours=9-16 weekdays=1-5",
155  	            str(rule.DateSpecValue("hours=9-16 weekdays=1-5")),
156  	        )
157  	        for item in rule.DateCommonValue.allowed_items:
158  	            self.assertSyntaxError(
159  	                "invalid %s '%s' in 'date-spec'" % (item, "foo"),
160  	                "%s=foo" % item,
161  	                rule.DateSpecValue,
162  	            )
163  	            self.assertSyntaxError(
164  	                "invalid %s '%s' in 'date-spec'" % (item, "1-foo"),
165  	                "%s=1-foo" % item,
166  	                rule.DateSpecValue,
167  	            )
168  	            self.assertSyntaxError(
169  	                "invalid %s '%s' in 'date-spec'" % (item, "foo-1"),
170  	                "%s=foo-1" % item,
171  	                rule.DateSpecValue,
172  	            )
173  	            self.assertSyntaxError(
174  	                "invalid %s '%s' in 'date-spec'" % (item, "1-2-3"),
175  	                "%s=1-2-3" % item,
176  	                rule.DateSpecValue,
177  	            )
178  	            self.assertSyntaxError(
179  	                "invalid %s '%s' in 'date-spec'" % (item, "2-1"),
180  	                "%s=2-1" % item,
181  	                rule.DateSpecValue,
182  	            )
183  	        self.assertSyntaxError(
184  	            "invalid hours '24' in 'date-spec'", "hours=24", rule.DateSpecValue
185  	        )
186  	        self.assertSyntaxError(
187  	            "invalid monthdays '32' in 'date-spec'",
188  	            "monthdays=32",
189  	            rule.DateSpecValue,
190  	        )
191  	        self.assertSyntaxError(
192  	            "invalid weekdays '8' in 'date-spec'",
193  	            "weekdays=8",
194  	            rule.DateSpecValue,
195  	        )
196  	        self.assertSyntaxError(
197  	            "invalid yeardays '367' in 'date-spec'",
198  	            "yeardays=367",
199  	            rule.DateSpecValue,
200  	        )
201  	        self.assertSyntaxError(
202  	            "invalid months '13' in 'date-spec'",
203  	            "months=13",
204  	            rule.DateSpecValue,
205  	        )
206  	        self.assertSyntaxError(
207  	            "invalid weeks '54' in 'date-spec'", "weeks=54", rule.DateSpecValue
208  	        )
209  	        self.assertSyntaxError(
210  	            "invalid weekyears '54' in 'date-spec'",
211  	            "weekyears=54",
212  	            rule.DateSpecValue,
213  	        )
214  	        self.assertSyntaxError(
215  	            "invalid moon '8' in 'date-spec'", "moon=8", rule.DateSpecValue
216  	        )
217  	        self.assertSyntaxError(
218  	            "invalid hours '12-8' in 'date-spec'",
219  	            "hours=12-8",
220  	            rule.DateSpecValue,
221  	        )
222  	
223  	    def assertSyntaxError(self, syntax_error, parts_string, value_class=None):
224  	        value_class = value_class if value_class else rule.DateCommonValue
225  	        self.assertRaises(rule.SyntaxError, value_class, parts_string)
226  	        try:
227  	            value_class(parts_string)
228  	        except rule.SyntaxError as e:
229  	            self.assertEqual(syntax_error, str(e))
230  	
231  	
232  	# already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
233  	class ParserTest(TestCase):
234  	    def setUp(self):
235  	        self.parser = rule.RuleParser()
236  	
237  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
238  	    def testEmptyInput(self):
239  	        self.assertRaises(rule.UnexpectedEndOfInput, self.parser.parse, [])
240  	
241  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
242  	    def testSingleLiteral(self):
243  	        self.assertSyntaxError(
244  	            "missing one of 'eq', 'ne', 'lt', 'gt', 'lte', 'gte', 'in_range', "
245  	            "'defined', 'not_defined', 'date-spec'",
246  	            ["#uname"],
247  	        )
248  	        self.assertSyntaxError(
249  	            "missing one of 'eq', 'ne', 'lt', 'gt', 'lte', 'gte', 'in_range', "
250  	            "'defined', 'not_defined', 'date-spec'",
251  	            ["string", "node1"],
252  	        )
253  	
254  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
255  	    def testSingleLiteralDatespec(self):
256  	        self.assertEqual(
257  	            "(date-spec (literal hours=1))",
258  	            str(self.parser.parse(["date-spec", "hours=1"])),
259  	        )
260  	        self.assertEqual(
261  	            "(date-spec (literal hours=1-14 monthdays=20-30 months=1))",
262  	            str(
263  	                self.parser.parse(
264  	                    ["date-spec", "hours=1-14 months=1 monthdays=20-30"]
265  	                )
266  	            ),
267  	        )
268  	        self.assertUnexpectedEndOfInput(["date-spec"])
269  	
270  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
271  	    def testSimpleExpression(self):
272  	        self.assertEqual(
273  	            "(eq (literal #uname) (literal node1))",
274  	            str(self.parser.parse(["#uname", "eq", "node1"])),
275  	        )
276  	        self.assertEqual(
277  	            "(ne (literal #uname) (literal node2))",
278  	            str(self.parser.parse(["#uname", "ne", "node2"])),
279  	        )
280  	        self.assertEqual(
281  	            "(gt (literal int) (literal 123))",
282  	            str(self.parser.parse(["int", "gt", "123"])),
283  	        )
284  	        self.assertEqual(
285  	            "(gte (literal int) (literal 123))",
286  	            str(self.parser.parse(["int", "gte", "123"])),
287  	        )
288  	        self.assertEqual(
289  	            "(lt (literal int) (literal 123))",
290  	            str(self.parser.parse(["int", "lt", "123"])),
291  	        )
292  	        self.assertEqual(
293  	            "(lte (literal int) (literal 123))",
294  	            str(self.parser.parse(["int", "lte", "123"])),
295  	        )
296  	
297  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
298  	    def testSimpleExpressionBad(self):
299  	        self.assertSyntaxError("unexpected 'eq'", ["eq"])
300  	        self.assertUnexpectedEndOfInput(["#uname", "eq"])
301  	        self.assertSyntaxError("unexpected 'node1'", ["#uname", "node1"])
302  	        self.assertSyntaxError("unexpected 'eq'", ["eq", "#uname"])
303  	        self.assertSyntaxError("unexpected 'eq'", ["eq", "lt"])
304  	        self.assertSyntaxError(
305  	            "unexpected 'string' before 'eq'",
306  	            ["string", "#uname", "eq", "node1"],
307  	        )
308  	        self.assertSyntaxError(
309  	            "unexpected 'date-spec' before 'eq'",
310  	            ["date-spec", "hours=1", "eq", "node1"],
311  	        )
312  	        self.assertSyntaxError(
313  	            "unexpected 'date-spec' after 'eq'",
314  	            ["#uname", "eq", "date-spec", "hours=1"],
315  	        )
316  	        self.assertSyntaxError(
317  	            "unexpected 'duration' before 'eq'",
318  	            ["duration", "hours=1", "eq", "node1"],
319  	        )
320  	        self.assertSyntaxError(
321  	            "unexpected 'duration' after 'eq'",
322  	            ["#uname", "eq", "duration", "hours=1"],
323  	        )
324  	
325  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
326  	    def testDefinedExpression(self):
327  	        self.assertEqual(
328  	            "(defined (literal pingd))",
329  	            str(self.parser.parse(["defined", "pingd"])),
330  	        )
331  	        self.assertEqual(
332  	            "(not_defined (literal pingd))",
333  	            str(self.parser.parse(["not_defined", "pingd"])),
334  	        )
335  	
336  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
337  	    def testDefinedExpressionBad(self):
338  	        self.assertUnexpectedEndOfInput(["defined"])
339  	        self.assertUnexpectedEndOfInput(["not_defined"])
340  	        self.assertSyntaxError("unexpected 'eq'", ["defined", "eq"])
341  	        self.assertSyntaxError("unexpected 'and'", ["defined", "and"])
342  	        self.assertSyntaxError(
343  	            "unexpected 'string' after 'defined'",
344  	            ["defined", "string", "pingd"],
345  	        )
346  	        self.assertSyntaxError(
347  	            "unexpected 'date-spec' after 'defined'",
348  	            ["defined", "date-spec", "hours=1"],
349  	        )
350  	        self.assertSyntaxError(
351  	            "unexpected 'duration' after 'defined'",
352  	            ["defined", "duration", "hours=1"],
353  	        )
354  	
355  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
356  	    def testTypeExpression(self):
357  	        self.assertEqual(
358  	            "(eq (literal #uname) (string (literal node1)))",
359  	            str(self.parser.parse(["#uname", "eq", "string", "node1"])),
360  	        )
361  	        self.assertEqual(
362  	            "(eq (literal #uname) (integer (literal 12345)))",
363  	            str(self.parser.parse(["#uname", "eq", "integer", "12345"])),
364  	        )
365  	        self.assertEqual(
366  	            "(eq (literal #uname) (integer (literal -12345)))",
367  	            str(self.parser.parse(["#uname", "eq", "integer", "-12345"])),
368  	        )
369  	        self.assertEqual(
370  	            "(eq (literal #uname) (number (literal 12345)))",
371  	            str(self.parser.parse(["#uname", "eq", "number", "12345"])),
372  	        )
373  	        self.assertEqual(
374  	            "(eq (literal #uname) (number (literal 12.345)))",
375  	            str(self.parser.parse(["#uname", "eq", "number", "12.345"])),
376  	        )
377  	        self.assertEqual(
378  	            "(eq (literal #uname) (number (literal 12345.)))",
379  	            str(self.parser.parse(["#uname", "eq", "number", "12345."])),
380  	        )
381  	        self.assertEqual(
382  	            "(eq (literal #uname) (number (literal .12345)))",
383  	            str(self.parser.parse(["#uname", "eq", "number", ".12345"])),
384  	        )
385  	        self.assertEqual(
386  	            "(eq (literal #uname) (number (literal 123e45)))",
387  	            str(self.parser.parse(["#uname", "eq", "number", "123e45"])),
388  	        )
389  	        self.assertEqual(
390  	            "(eq (literal #uname) (number (literal 123E45)))",
391  	            str(self.parser.parse(["#uname", "eq", "number", "123E45"])),
392  	        )
393  	        self.assertEqual(
394  	            "(eq (literal #uname) (number (literal 123e+45)))",
395  	            str(self.parser.parse(["#uname", "eq", "number", "123e+45"])),
396  	        )
397  	        self.assertEqual(
398  	            "(eq (literal #uname) (number (literal 123E-45)))",
399  	            str(self.parser.parse(["#uname", "eq", "number", "123E-45"])),
400  	        )
401  	        self.assertEqual(
402  	            "(eq (literal #uname) (number (literal 12.34e5)))",
403  	            str(self.parser.parse(["#uname", "eq", "number", "12.34e5"])),
404  	        )
405  	        self.assertEqual(
406  	            "(eq (literal #uname) (number (literal +12.34e5)))",
407  	            str(self.parser.parse(["#uname", "eq", "number", "+12.34e5"])),
408  	        )
409  	        self.assertEqual(
410  	            "(eq (literal #uname) (number (literal -12.34e5)))",
411  	            str(self.parser.parse(["#uname", "eq", "number", "-12.34e5"])),
412  	        )
413  	        self.assertEqual(
414  	            "(eq (literal #uname) (version (literal 1)))",
415  	            str(self.parser.parse(["#uname", "eq", "version", "1"])),
416  	        )
417  	        self.assertEqual(
418  	            "(eq (literal #uname) (version (literal 1.2.3)))",
419  	            str(self.parser.parse(["#uname", "eq", "version", "1.2.3"])),
420  	        )
421  	        self.assertEqual(
422  	            "(eq (literal #uname) (string (literal string)))",
423  	            str(self.parser.parse(["#uname", "eq", "string", "string"])),
424  	        )
425  	        self.assertEqual(
426  	            "(eq (literal #uname) (string (literal and)))",
427  	            str(self.parser.parse(["#uname", "eq", "string", "and"])),
428  	        )
429  	        self.assertEqual(
430  	            "(and "
431  	            "(ne (literal #uname) (string (literal integer))) "
432  	            "(ne (literal #uname) (string (literal version)))"
433  	            ")",
434  	            str(
435  	                self.parser.parse(
436  	                    [
437  	                        "#uname",
438  	                        "ne",
439  	                        "string",
440  	                        "integer",
441  	                        "and",
442  	                        "#uname",
443  	                        "ne",
444  	                        "string",
445  	                        "version",
446  	                    ]
447  	                )
448  	            ),
449  	        )
450  	
451  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
452  	    # and pcs_test/tier0/lib/cib/rule/test_validator.py
453  	    def testTypeExpressionBad(self):
454  	        self.assertUnexpectedEndOfInput(["string"])
455  	        self.assertUnexpectedEndOfInput(["#uname", "eq", "string"])
456  	        self.assertSyntaxError(
457  	            "unexpected 'string' before 'eq'",
458  	            ["string", "#uname", "eq", "node1"],
459  	        )
460  	        self.assertSyntaxError(
461  	            "invalid integer value '123.45'",
462  	            ["#uname", "eq", "integer", "123.45"],
463  	        )
464  	        self.assertSyntaxError(
465  	            "invalid number value '123e45E67'",
466  	            ["#uname", "eq", "number", "123e45E67"],
467  	        )
468  	        self.assertSyntaxError(
469  	            "invalid number value '123.45.67'",
470  	            ["#uname", "eq", "number", "123.45.67"],
471  	        )
472  	        self.assertSyntaxError(
473  	            "invalid version value 'node1'",
474  	            ["#uname", "eq", "version", "node1"],
475  	        )
476  	
477  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
478  	    def testDateExpression(self):
479  	        self.assertEqual(
480  	            "(gt (literal date) (literal 2014-06-26))",
481  	            str(self.parser.parse(["date", "gt", "2014-06-26"])),
482  	        )
483  	        self.assertEqual(
484  	            "(gt (literal date) (literal 2014-06-26 12:00:00))",
485  	            str(self.parser.parse(["date", "gt", "2014-06-26 12:00:00"])),
486  	        )
487  	        self.assertEqual(
488  	            "(lt (literal date) (literal 2014-06-26))",
489  	            str(self.parser.parse(["date", "lt", "2014-06-26"])),
490  	        )
491  	        self.assertEqual(
492  	            "(lt (literal date) (literal 2014-06-26 12:00:00))",
493  	            str(self.parser.parse(["date", "lt", "2014-06-26 12:00:00"])),
494  	        )
495  	        self.assertEqual(
496  	            "(in_range "
497  	            "(literal date) (literal 2014-06-26) (literal 2014-07-26)"
498  	            ")",
499  	            str(
500  	                self.parser.parse(
501  	                    ["date", "in_range", "2014-06-26", "to", "2014-07-26"]
502  	                )
503  	            ),
504  	        )
505  	        self.assertEqual(
506  	            "(in_range "
507  	            "(literal date) (literal 2014-06-26 12:00) (literal 2014-07-26 13:00)"
508  	            ")",
509  	            str(
510  	                self.parser.parse(
511  	                    [
512  	                        "date",
513  	                        "in_range",
514  	                        "2014-06-26 12:00",
515  	                        "to",
516  	                        "2014-07-26 13:00",
517  	                    ]
518  	                )
519  	            ),
520  	        )
521  	        self.assertEqual(
522  	            "(in_range "
523  	            "(literal date) "
524  	            "(literal 2014-06-26) (duration (literal years=1))"
525  	            ")",
526  	            str(
527  	                self.parser.parse(
528  	                    [
529  	                        "date",
530  	                        "in_range",
531  	                        "2014-06-26",
532  	                        "to",
533  	                        "duration",
534  	                        "years=1",
535  	                    ]
536  	                )
537  	            ),
538  	        )
539  	
540  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
541  	    # and pcs_test/tier0/lib/cib/rule/test_validator.py
542  	    def testDateExpressionBad(self):
543  	        self.assertUnexpectedEndOfInput(["date", "in_range"])
544  	        self.assertSyntaxError(
545  	            "missing 'to'", ["date", "in_range", "2014-06-26"]
546  	        )
547  	        self.assertUnexpectedEndOfInput(
548  	            ["date", "in_range", "2014-06-26", "to"]
549  	        )
550  	        self.assertSyntaxError(
551  	            "unexpected 'in_range'",
552  	            ["in_range", "2014-06-26", "to", "2014-07-26"],
553  	        )
554  	        self.assertSyntaxError(
555  	            "expecting 'to', got 'eq'",
556  	            ["date", "in_range", "#uname", "eq", "node1", "to", "2014-07-26"],
557  	        )
558  	        self.assertSyntaxError(
559  	            "invalid date '#uname' in 'in_range ... to'",
560  	            ["date", "in_range", "2014-06-26", "to", "#uname", "eq", "node1"],
561  	        )
562  	        self.assertSyntaxError(
563  	            "unexpected 'defined' after 'in_range'",
564  	            ["date", "in_range", "defined", "pingd", "to", "2014-07-26"],
565  	        )
566  	        self.assertSyntaxError(
567  	            "unexpected 'defined' after 'in_range ... to'",
568  	            ["date", "in_range", "2014-06-26", "to", "defined", "pingd"],
569  	        )
570  	        self.assertSyntaxError(
571  	            "unexpected 'string' before 'in_range'",
572  	            ["string", "date", "in_range", "2014-06-26", "to", "2014-07-26"],
573  	        )
574  	        self.assertSyntaxError(
575  	            "unexpected 'string' after 'in_range'",
576  	            ["date", "in_range", "string", "2014-06-26", "to", "2014-07-26"],
577  	        )
578  	        self.assertSyntaxError(
579  	            "unexpected 'string' after 'in_range ... to'",
580  	            ["date", "in_range", "2014-06-26", "to", "string", "2014-07-26"],
581  	        )
582  	        self.assertSyntaxError(
583  	            "unexpected 'string' after '2014-06-26'",
584  	            ["date", "in_range", "2014-06-26", "string", "to", "2014-07-26"],
585  	        )
586  	        self.assertSyntaxError(
587  	            "unexpected '#uname' before 'in_range'",
588  	            ["#uname", "in_range", "2014-06-26", "to", "2014-07-26"],
589  	        )
590  	        self.assertSyntaxError(
591  	            "invalid date '2014-13-26' in 'in_range ... to'",
592  	            ["date", "in_range", "2014-13-26", "to", "2014-07-26"],
593  	        )
594  	        self.assertSyntaxError(
595  	            "invalid date '2014-13-26' in 'in_range ... to'",
596  	            ["date", "in_range", "2014-06-26", "to", "2014-13-26"],
597  	        )
598  	
599  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
600  	    def testAndOrExpression(self):
601  	        self.assertEqual(
602  	            "(and "
603  	            "(ne (literal #uname) (literal node1)) "
604  	            "(ne (literal #uname) (literal node2))"
605  	            ")",
606  	            str(
607  	                self.parser.parse(
608  	                    ["#uname", "ne", "node1", "and", "#uname", "ne", "node2"]
609  	                )
610  	            ),
611  	        )
612  	        self.assertEqual(
613  	            "(or "
614  	            "(eq (literal #uname) (literal node1)) "
615  	            "(eq (literal #uname) (literal node2))"
616  	            ")",
617  	            str(
618  	                self.parser.parse(
619  	                    ["#uname", "eq", "node1", "or", "#uname", "eq", "node2"]
620  	                )
621  	            ),
622  	        )
623  	        self.assertEqual(
624  	            "(and "
625  	            "(and "
626  	            "(ne (literal #uname) (literal node1)) "
627  	            "(ne (literal #uname) (literal node2))"
628  	            ") "
629  	            "(ne (literal #uname) (literal node3))"
630  	            ")",
631  	            str(
632  	                self.parser.parse(
633  	                    [
634  	                        "#uname",
635  	                        "ne",
636  	                        "node1",
637  	                        "and",
638  	                        "#uname",
639  	                        "ne",
640  	                        "node2",
641  	                        "and",
642  	                        "#uname",
643  	                        "ne",
644  	                        "node3",
645  	                    ]
646  	                )
647  	            ),
648  	        )
649  	        self.assertEqual(
650  	            "(or "
651  	            "(and "
652  	            "(ne (literal #uname) (literal node1)) "
653  	            "(ne (literal #uname) (literal node2))"
654  	            ") "
655  	            "(eq (literal #uname) (literal node3))"
656  	            ")",
657  	            str(
658  	                self.parser.parse(
659  	                    [
660  	                        "#uname",
661  	                        "ne",
662  	                        "node1",
663  	                        "and",
664  	                        "#uname",
665  	                        "ne",
666  	                        "node2",
667  	                        "or",
668  	                        "#uname",
669  	                        "eq",
670  	                        "node3",
671  	                    ]
672  	                )
673  	            ),
674  	        )
675  	        self.assertEqual(
676  	            "(and "
677  	            "(or "
678  	            "(eq (literal #uname) (literal node1)) "
679  	            "(eq (literal #uname) (literal node2))"
680  	            ") "
681  	            "(ne (literal #uname) (literal node3))"
682  	            ")",
683  	            str(
684  	                self.parser.parse(
685  	                    [
686  	                        "#uname",
687  	                        "eq",
688  	                        "node1",
689  	                        "or",
690  	                        "#uname",
691  	                        "eq",
692  	                        "node2",
693  	                        "and",
694  	                        "#uname",
695  	                        "ne",
696  	                        "node3",
697  	                    ]
698  	                )
699  	            ),
700  	        )
701  	        self.assertEqual(
702  	            "(and (defined (literal pingd)) (lte (literal pingd) (literal 1)))",
703  	            str(
704  	                self.parser.parse(
705  	                    ["defined", "pingd", "and", "pingd", "lte", "1"]
706  	                )
707  	            ),
708  	        )
709  	        self.assertEqual(
710  	            "(or "
711  	            "(gt (literal pingd) (literal 1)) "
712  	            "(not_defined (literal pingd))"
713  	            ")",
714  	            str(
715  	                self.parser.parse(
716  	                    ["pingd", "gt", "1", "or", "not_defined", "pingd"]
717  	                )
718  	            ),
719  	        )
720  	
721  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
722  	    def testAndOrExpressionDateSpec(self):
723  	        self.assertEqual(
724  	            "(and "
725  	            "(ne (literal #uname) (literal node1)) "
726  	            "(date-spec (literal hours=1-12))"
727  	            ")",
728  	            str(
729  	                self.parser.parse(
730  	                    ["#uname", "ne", "node1", "and", "date-spec", "hours=1-12"]
731  	                )
732  	            ),
733  	        )
734  	        self.assertEqual(
735  	            "(or "
736  	            "(date-spec (literal monthdays=1-12)) "
737  	            "(ne (literal #uname) (literal node1))"
738  	            ")",
739  	            str(
740  	                self.parser.parse(
741  	                    [
742  	                        "date-spec",
743  	                        "monthdays=1-12",
744  	                        "or",
745  	                        "#uname",
746  	                        "ne",
747  	                        "node1",
748  	                    ]
749  	                )
750  	            ),
751  	        )
752  	        self.assertEqual(
753  	            "(or "
754  	            "(date-spec (literal monthdays=1-10)) "
755  	            "(date-spec (literal monthdays=11-20))"
756  	            ")",
757  	            str(
758  	                self.parser.parse(
759  	                    [
760  	                        "date-spec",
761  	                        "monthdays=1-10",
762  	                        "or",
763  	                        "date-spec",
764  	                        "monthdays=11-20",
765  	                    ]
766  	                )
767  	            ),
768  	        )
769  	
770  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
771  	    def testAndOrExpressionDate(self):
772  	        self.assertEqual(
773  	            "(and "
774  	            "(ne (literal #uname) (literal node1)) "
775  	            "(in_range "
776  	            "(literal date) (literal 2014-06-26) (literal 2014-07-26)"
777  	            ")"
778  	            ")",
779  	            str(
780  	                self.parser.parse(
781  	                    [
782  	                        "#uname",
783  	                        "ne",
784  	                        "node1",
785  	                        "and",
786  	                        "date",
787  	                        "in_range",
788  	                        "2014-06-26",
789  	                        "to",
790  	                        "2014-07-26",
791  	                    ]
792  	                )
793  	            ),
794  	        )
795  	        self.assertEqual(
796  	            "(and "
797  	            "(in_range "
798  	            "(literal date) (literal 2014-06-26) (literal 2014-07-26)"
799  	            ") "
800  	            "(ne (literal #uname) (literal node1))"
801  	            ")",
802  	            str(
803  	                self.parser.parse(
804  	                    [
805  	                        "date",
806  	                        "in_range",
807  	                        "2014-06-26",
808  	                        "to",
809  	                        "2014-07-26",
810  	                        "and",
811  	                        "#uname",
812  	                        "ne",
813  	                        "node1",
814  	                    ]
815  	                )
816  	            ),
817  	        )
818  	
819  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
820  	    @mock.patch("pcs.rule.deprecation_warning", mock.Mock())
821  	    def testAndOrExpressionBad(self):
822  	        self.assertSyntaxError("unexpected 'and'", ["and"])
823  	        self.assertSyntaxError("unexpected 'or'", ["or"])
824  	        self.assertSyntaxError(
825  	            "unexpected '#uname' before 'and'", ["#uname", "and", "node1"]
826  	        )
827  	        self.assertSyntaxError(
828  	            "unexpected '#uname' before 'or'", ["#uname", "or", "node1"]
829  	        )
830  	        self.assertSyntaxError(
831  	            "unexpected '#uname' before 'or'", ["#uname", "or", "eq"]
832  	        )
833  	        self.assertSyntaxError(
834  	            "unexpected 'node2' after 'and'",
835  	            ["#uname", "eq", "node1", "and", "node2"],
836  	        )
837  	        self.assertUnexpectedEndOfInput(["#uname", "eq", "node1", "and"])
838  	        self.assertUnexpectedEndOfInput(
839  	            ["#uname", "eq", "node1", "and", "#uname", "eq"]
840  	        )
841  	        self.assertSyntaxError(
842  	            "unexpected 'and'", ["and", "#uname", "eq", "node1"]
843  	        )
844  	        self.assertSyntaxError(
845  	            "unexpected 'duration' after 'and'",
846  	            ["#uname", "ne", "node1", "and", "duration", "hours=1"],
847  	        )
848  	        self.assertSyntaxError(
849  	            "unexpected 'duration' before 'or'",
850  	            ["duration", "monthdays=1", "or", "#uname", "ne", "node1"],
851  	        )
852  	
853  	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
854  	    def testParenthesizedExpression(self):
855  	        self.assertSyntaxError(
856  	            "missing one of 'eq', 'ne', 'lt', 'gt', 'lte', 'gte', 'in_range', "
857  	            "'defined', 'not_defined', 'date-spec'",
858  	            ["(", "#uname", ")"],
859  	        )
860  	        self.assertEqual(
861  	            "(date-spec (literal hours=1))",
862  	            str(self.parser.parse(["(", "date-spec", "hours=1", ")"])),
863  	        )
864  	        self.assertEqual(
865  	            "(eq (literal #uname) (literal node1))",
866  	            str(self.parser.parse(["(", "#uname", "eq", "node1", ")"])),
867  	        )
868  	        self.assertEqual(
869  	            "(defined (literal pingd))",
870  	            str(self.parser.parse(["(", "defined", "pingd", ")"])),
871  	        )
872  	        self.assertEqual(
873  	            "(and "
874  	            "(ne (literal #uname) (literal node1)) "
875  	            "(ne (literal #uname) (literal node2))"
876  	            ")",
877  	            str(
878  	                self.parser.parse(
879  	                    [
880  	                        "(",
881  	                        "#uname",
882  	                        "ne",
883  	                        "node1",
884  	                        "and",
885  	                        "#uname",
886  	                        "ne",
887  	                        "node2",
888  	                        ")",
889  	                    ]
890  	                )
891  	            ),
892  	        )
893  	        self.assertEqual(
894  	            "(and "
895  	            "(ne (literal #uname) (literal node1)) "
896  	            "(ne (literal #uname) (literal node2))"
897  	            ")",
898  	            str(
899  	                self.parser.parse(
900  	                    [
901  	                        "(",
902  	                        "#uname",
903  	                        "ne",
904  	                        "node1",
905  	                        ")",
906  	                        "and",
907  	                        "(",
908  	                        "#uname",
909  	                        "ne",
910  	                        "node2",
911  	                        ")",
912  	                    ]
913  	                )
914  	            ),
915  	        )
916  	        self.assertEqual(
917  	            "(or "
918  	            "(and "
919  	            "(ne (literal #uname) (literal node1)) "
920  	            "(ne (literal #uname) (literal node2))"
921  	            ") "
922  	            "(eq (literal #uname) (literal node3))"
923  	            ")",
924  	            str(
925  	                self.parser.parse(
926  	                    [
927  	                        "(",
928  	                        "#uname",
929  	                        "ne",
930  	                        "node1",
931  	                        "and",
932  	                        "#uname",
933  	                        "ne",
934  	                        "node2",
935  	                        ")",
936  	                        "or",
937  	                        "#uname",
938  	                        "eq",
939  	                        "node3",
940  	                    ]
941  	                )
942  	            ),
943  	        )
944  	        self.assertEqual(
945  	            "(and "
946  	            "(ne (literal #uname) (literal node1)) "
947  	            "(or "
948  	            "(ne (literal #uname) (literal node2)) "
949  	            "(eq (literal #uname) (literal node3))"
950  	            ")"
951  	            ")",
952  	            str(
953  	                self.parser.parse(
954  	                    [
955  	                        "#uname",
956  	                        "ne",
957  	                        "node1",
958  	                        "and",
959  	                        "(",
960  	                        "#uname",
961  	                        "ne",
962  	                        "node2",
963  	                        "or",
964  	                        "#uname",
965  	                        "eq",
966  	                        "node3",
967  	                        ")",
968  	                    ]
969  	                )
970  	            ),
971  	        )
972  	        self.assertEqual(
973  	            "(and "
974  	            "(ne (literal #uname) (literal node1)) "
975  	            "(or "
976  	            "(ne (literal #uname) (literal node2)) "
977  	            "(eq (literal #uname) (literal node3))"
978  	            ")"
979  	            ")",
980  	            str(
981  	                self.parser.parse(
982  	                    [
983  	                        "(",
984  	                        "(",
985  	                        "(",
986  	                        "#uname",
987  	                        "ne",
988  	                        "node1",
989  	                        ")",
990  	                        "and",
991  	                        "(",
992  	                        "(",
993  	                        "(",
994  	                        "#uname",
995  	                        "ne",
996  	                        "node2",
997  	                        ")",
998  	                        "or",
999  	                        "(",
1000 	                        "#uname",
1001 	                        "eq",
1002 	                        "node3",
1003 	                        ")",
1004 	                        ")",
1005 	                        ")",
1006 	                        ")",
1007 	                        ")",
1008 	                    ]
1009 	                )
1010 	            ),
1011 	        )
1012 	        self.assertEqual(
1013 	            "(in_range "
1014 	            "(literal date) (literal 2014-06-26) (literal 2014-07-26)"
1015 	            ")",
1016 	            str(
1017 	                self.parser.parse(
1018 	                    [
1019 	                        "(",
1020 	                        "date",
1021 	                        "in_range",
1022 	                        "2014-06-26",
1023 	                        "to",
1024 	                        "2014-07-26",
1025 	                        ")",
1026 	                    ]
1027 	                )
1028 	            ),
1029 	        )
1030 	
1031 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
1032 	    def testParenthesizedExpressionBad(self):
1033 	        self.assertUnexpectedEndOfInput(["("])
1034 	        self.assertSyntaxError("unexpected ')'", ["(", ")"])
1035 	        self.assertSyntaxError("missing ')'", ["(", "#uname"])
1036 	        self.assertUnexpectedEndOfInput(["(", "#uname", "eq"])
1037 	        self.assertSyntaxError("missing ')'", ["(", "#uname", "eq", "node1"])
1038 	
1039 	    def assertUnexpectedEndOfInput(self, program):
1040 	        self.assertRaises(rule.UnexpectedEndOfInput, self.parser.parse, program)
1041 	
1042 	    def assertSyntaxError(self, syntax_error, program):
1043 	        self.assertRaises(rule.SyntaxError, self.parser.parse, program)
1044 	        try:
1045 	            self.parser.parse(program)
1046 	        except rule.SyntaxError as e:
1047 	            self.assertEqual(syntax_error, str(e))
1048 	
1049 	
1050 	# already moved to pcs_test/tier0/lib/cib/rule/test_tools.py
1051 	class HasNodeAttrExprWithTypeInteger(TestCase):
1052 	    @staticmethod
1053 	    def fixture_has_integer(rule_expression):
1054 	        return rule.has_node_attr_expr_with_type_integer(
1055 	            rule.RuleParser().parse(
1056 	                rule.TokenPreprocessor().run(rule_expression)
1057 	            )
1058 	        )
1059 	
1060 	    def test_node_attr_no_type(self):
1061 	        self.assertFalse(
1062 	            self.fixture_has_integer(
1063 	                [
1064 	                    "(",
1065 	                    "a",
1066 	                    "eq",
1067 	                    "A",
1068 	                    "and",
1069 	                    "b",
1070 	                    "eq",
1071 	                    "123",
1072 	                    ")",
1073 	                    "or",
1074 	                    "a",
1075 	                    "eq",
1076 	                    "AA",
1077 	                ]
1078 	            )
1079 	        )
1080 	
1081 	    def test_node_attr_no_integer(self):
1082 	        self.assertFalse(
1083 	            self.fixture_has_integer(
1084 	                [
1085 	                    "(",
1086 	                    "a",
1087 	                    "eq",
1088 	                    "A",
1089 	                    "and",
1090 	                    "b",
1091 	                    "eq",
1092 	                    "number",
1093 	                    "123",
1094 	                    ")",
1095 	                    "or",
1096 	                    "a",
1097 	                    "eq",
1098 	                    "AA",
1099 	                ]
1100 	            )
1101 	        )
1102 	
1103 	    def test_node_attr_integer(self):
1104 	        self.assertTrue(
1105 	            self.fixture_has_integer(
1106 	                [
1107 	                    "(",
1108 	                    "a",
1109 	                    "eq",
1110 	                    "A",
1111 	                    "and",
1112 	                    "b",
1113 	                    "eq",
1114 	                    "integer",
1115 	                    "123",
1116 	                    ")",
1117 	                    "or",
1118 	                    "a",
1119 	                    "eq",
1120 	                    "AA",
1121 	                ]
1122 	            )
1123 	        )
1124 	
1125 	    def test_node_attr_integer_not_date_expression(self):
1126 	        self.assertTrue(
1127 	            self.fixture_has_integer(["date", "gt", "integer", "123"])
1128 	        )
1129 	
1130 	    def test_no_node_attr(self):
1131 	        self.assertFalse(
1132 	            self.fixture_has_integer(
1133 	                [
1134 	                    "(",
1135 	                    "date-spec",
1136 	                    "hours=1",
1137 	                    "or",
1138 	                    "defined",
1139 	                    "pingd",
1140 	                    ")",
1141 	                    "or",
1142 	                    "(",
1143 	                    "date",
1144 	                    "gt",
1145 	                    "2014-06-26",
1146 	                    "or",
1147 	                    "date",
1148 	                    "in_range",
1149 	                    "2014-06-26",
1150 	                    "to",
1151 	                    "2014-07-26",
1152 	                    ")",
1153 	                ]
1154 	            )
1155 	        )
1156 	
1157 	
1158 	# already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1159 	class CibBuilderTest(TestCase):
1160 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1161 	    def testSingleLiteralDatespec(self):
1162 	        self.assertExpressionXml(
1163 	            ["date-spec", "hours=1"],
1164 	            """
1165 	<rsc_location id="location-dummy">
1166 	    <rule id="location-dummy-rule">
1167 	        <date_expression id="location-dummy-rule-expr" operation="date_spec">
1168 	            <date_spec hours="1" id="location-dummy-rule-expr-datespec"/>
1169 	        </date_expression>
1170 	    </rule>
1171 	</rsc_location>
1172 	            """,
1173 	        )
1174 	        self.assertExpressionXml(
1175 	            ["date-spec", "hours=1-14 monthdays=20-30 months=1"],
1176 	            """
1177 	<rsc_location id="location-dummy">
1178 	    <rule id="location-dummy-rule">
1179 	        <date_expression id="location-dummy-rule-expr" operation="date_spec">
1180 	            <date_spec hours="1-14" id="location-dummy-rule-expr-datespec" monthdays="20-30" months="1"/>
1181 	        </date_expression>
1182 	    </rule>
1183 	</rsc_location>
1184 	            """,
1185 	        )
1186 	
1187 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1188 	    def testSimpleExpression(self):
1189 	        self.assertExpressionXml(
1190 	            ["#uname", "eq", "node1"],
1191 	            """
1192 	<rsc_location id="location-dummy">
1193 	    <rule id="location-dummy-rule">
1194 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
1195 	    </rule>
1196 	</rsc_location>
1197 	            """,
1198 	        )
1199 	        self.assertExpressionXml(
1200 	            ["#uname", "ne", "node1"],
1201 	            """
1202 	<rsc_location id="location-dummy">
1203 	    <rule id="location-dummy-rule">
1204 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node1"/>
1205 	    </rule>
1206 	</rsc_location>
1207 	            """,
1208 	        )
1209 	        self.assertExpressionXml(
1210 	            ["#uname", "gt", "node1"],
1211 	            """
1212 	<rsc_location id="location-dummy">
1213 	    <rule id="location-dummy-rule">
1214 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="gt" value="node1"/>
1215 	    </rule>
1216 	</rsc_location>
1217 	            """,
1218 	        )
1219 	        self.assertExpressionXml(
1220 	            ["#uname", "gte", "node1"],
1221 	            """
1222 	<rsc_location id="location-dummy">
1223 	    <rule id="location-dummy-rule">
1224 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="gte" value="node1"/>
1225 	    </rule>
1226 	</rsc_location>
1227 	            """,
1228 	        )
1229 	        self.assertExpressionXml(
1230 	            ["#uname", "lt", "node1"],
1231 	            """
1232 	<rsc_location id="location-dummy">
1233 	    <rule id="location-dummy-rule">
1234 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="lt" value="node1"/>
1235 	    </rule>
1236 	</rsc_location>
1237 	            """,
1238 	        )
1239 	        self.assertExpressionXml(
1240 	            ["#uname", "lte", "node1"],
1241 	            """
1242 	<rsc_location id="location-dummy">
1243 	    <rule id="location-dummy-rule">
1244 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="lte" value="node1"/>
1245 	    </rule>
1246 	</rsc_location>
1247 	            """,
1248 	        )
1249 	
1250 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1251 	    def testTypeExpression(self):
1252 	        self.assertExpressionXml(
1253 	            ["#uname", "eq", "string", "node1"],
1254 	            """
1255 	<rsc_location id="location-dummy">
1256 	    <rule id="location-dummy-rule">
1257 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="string" value="node1"/>
1258 	    </rule>
1259 	</rsc_location>
1260 	            """,
1261 	        )
1262 	        self.assertExpressionXml(
1263 	            ["#uname", "eq", "number", "12345"],
1264 	            """
1265 	<rsc_location id="location-dummy">
1266 	    <rule id="location-dummy-rule">
1267 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="number" value="12345"/>
1268 	    </rule>
1269 	</rsc_location>
1270 	            """,
1271 	        )
1272 	        self.assertExpressionXml(
1273 	            ["#uname", "eq", "integer", "12345"],
1274 	            """
1275 	<rsc_location id="location-dummy">
1276 	    <rule id="location-dummy-rule">
1277 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="number" value="12345"/>
1278 	    </rule>
1279 	</rsc_location>
1280 	            """,
1281 	        )
1282 	        self.assertExpressionXml(
1283 	            ["#uname", "eq", "integer", "12345"],
1284 	            """
1285 	<rsc_location id="location-dummy">
1286 	    <rule id="location-dummy-rule">
1287 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="integer" value="12345"/>
1288 	    </rule>
1289 	</rsc_location>
1290 	            """,
1291 	            cib_file=rc("cib-empty-3.5.xml"),
1292 	        )
1293 	        self.assertExpressionXml(
1294 	            ["#uname", "eq", "version", "1.2.3"],
1295 	            """
1296 	<rsc_location id="location-dummy">
1297 	    <rule id="location-dummy-rule">
1298 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="version" value="1.2.3"/>
1299 	    </rule>
1300 	</rsc_location>
1301 	            """,
1302 	        )
1303 	
1304 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1305 	    def testDefinedExpression(self):
1306 	        self.assertExpressionXml(
1307 	            ["defined", "pingd"],
1308 	            """
1309 	<rsc_location id="location-dummy">
1310 	    <rule id="location-dummy-rule">
1311 	        <expression attribute="pingd" id="location-dummy-rule-expr" operation="defined"/>
1312 	    </rule>
1313 	</rsc_location>
1314 	            """,
1315 	        )
1316 	        self.assertExpressionXml(
1317 	            ["not_defined", "pingd"],
1318 	            """
1319 	<rsc_location id="location-dummy">
1320 	    <rule id="location-dummy-rule">
1321 	        <expression attribute="pingd" id="location-dummy-rule-expr" operation="not_defined"/>
1322 	    </rule>
1323 	</rsc_location>
1324 	            """,
1325 	        )
1326 	
1327 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1328 	    def testDateExpression(self):
1329 	        self.assertExpressionXml(
1330 	            ["date", "gt", "2014-06-26"],
1331 	            """
1332 	<rsc_location id="location-dummy">
1333 	    <rule id="location-dummy-rule">
1334 	        <date_expression id="location-dummy-rule-expr" operation="gt" start="2014-06-26"/>
1335 	    </rule>
1336 	</rsc_location>
1337 	            """,
1338 	        )
1339 	        self.assertExpressionXml(
1340 	            ["date", "lt", "2014-06-26"],
1341 	            """
1342 	<rsc_location id="location-dummy">
1343 	    <rule id="location-dummy-rule">
1344 	        <date_expression end="2014-06-26" id="location-dummy-rule-expr" operation="lt"/>
1345 	    </rule>
1346 	</rsc_location>
1347 	            """,
1348 	        )
1349 	        self.assertExpressionXml(
1350 	            ["date", "in_range", "2014-06-26", "to", "2014-07-26"],
1351 	            """
1352 	<rsc_location id="location-dummy">
1353 	    <rule id="location-dummy-rule">
1354 	        <date_expression end="2014-07-26" id="location-dummy-rule-expr" operation="in_range" start="2014-06-26"/>
1355 	    </rule>
1356 	</rsc_location>
1357 	            """,
1358 	        )
1359 	        self.assertExpressionXml(
1360 	            ["date", "in_range", "2014-06-26", "to", "duration", "years=1"],
1361 	            """
1362 	<rsc_location id="location-dummy">
1363 	    <rule id="location-dummy-rule">
1364 	        <date_expression id="location-dummy-rule-expr" operation="in_range" start="2014-06-26">
1365 	            <duration id="location-dummy-rule-expr-duration" years="1"/>
1366 	        </date_expression>
1367 	    </rule>
1368 	</rsc_location>
1369 	            """,
1370 	        )
1371 	
1372 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parser.py
1373 	    def testNotDateExpression(self):
1374 	        self.assertExpressionXml(
1375 	            ["date", "eq", "2014-06-26"],
1376 	            """
1377 	<rsc_location id="location-dummy">
1378 	    <rule id="location-dummy-rule">
1379 	        <expression attribute="date" id="location-dummy-rule-expr" operation="eq" value="2014-06-26"/>
1380 	    </rule>
1381 	</rsc_location>
1382 	            """,
1383 	        )
1384 	        self.assertExpressionXml(
1385 	            ["date", "gt", "string", "2014-06-26"],
1386 	            """
1387 	<rsc_location id="location-dummy">
1388 	    <rule id="location-dummy-rule">
1389 	        <expression attribute="date" id="location-dummy-rule-expr" operation="gt" type="string" value="2014-06-26"/>
1390 	    </rule>
1391 	</rsc_location>
1392 	            """,
1393 	        )
1394 	        self.assertExpressionXml(
1395 	            ["date", "gt", "number", "12345"],
1396 	            """
1397 	<rsc_location id="location-dummy">
1398 	    <rule id="location-dummy-rule">
1399 	        <expression attribute="date" id="location-dummy-rule-expr" operation="gt" type="number" value="12345"/>
1400 	    </rule>
1401 	</rsc_location>
1402 	            """,
1403 	        )
1404 	        self.assertExpressionXml(
1405 	            ["date", "gt", "version", "1.2.3"],
1406 	            """
1407 	<rsc_location id="location-dummy">
1408 	    <rule id="location-dummy-rule">
1409 	        <expression attribute="date" id="location-dummy-rule-expr" operation="gt" type="version" value="1.2.3"/>
1410 	    </rule>
1411 	</rsc_location>
1412 	            """,
1413 	        )
1414 	
1415 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1416 	    def testAndOrExpression(self):
1417 	        self.assertExpressionXml(
1418 	            ["#uname", "ne", "node1", "and", "#uname", "ne", "node2"],
1419 	            """
1420 	<rsc_location id="location-dummy">
1421 	    <rule boolean-op="and" id="location-dummy-rule">
1422 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node1"/>
1423 	        <expression attribute="#uname" id="location-dummy-rule-expr-1" operation="ne" value="node2"/>
1424 	    </rule>
1425 	</rsc_location>
1426 	            """,
1427 	        )
1428 	        self.assertExpressionXml(
1429 	            ["#uname", "eq", "node1", "or", "#uname", "eq", "node2"],
1430 	            """
1431 	<rsc_location id="location-dummy">
1432 	    <rule boolean-op="or" id="location-dummy-rule">
1433 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
1434 	        <expression attribute="#uname" id="location-dummy-rule-expr-1" operation="eq" value="node2"/>
1435 	    </rule>
1436 	</rsc_location>
1437 	            """,
1438 	        )
1439 	        self.assertExpressionXml(
1440 	            [
1441 	                "#uname",
1442 	                "ne",
1443 	                "node1",
1444 	                "and",
1445 	                "#uname",
1446 	                "ne",
1447 	                "node2",
1448 	                "and",
1449 	                "#uname",
1450 	                "ne",
1451 	                "node3",
1452 	            ],
1453 	            """
1454 	<rsc_location id="location-dummy">
1455 	    <rule boolean-op="and" id="location-dummy-rule">
1456 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node1"/>
1457 	        <expression attribute="#uname" id="location-dummy-rule-expr-1" operation="ne" value="node2"/>
1458 	        <expression attribute="#uname" id="location-dummy-rule-expr-2" operation="ne" value="node3"/>
1459 	    </rule>
1460 	</rsc_location>
1461 	            """,
1462 	        )
1463 	        self.assertExpressionXml(
1464 	            [
1465 	                "#uname",
1466 	                "ne",
1467 	                "node1",
1468 	                "and",
1469 	                "#uname",
1470 	                "ne",
1471 	                "node2",
1472 	                "or",
1473 	                "#uname",
1474 	                "eq",
1475 	                "node3",
1476 	            ],
1477 	            """
1478 	<rsc_location id="location-dummy">
1479 	    <rule boolean-op="or" id="location-dummy-rule">
1480 	        <rule boolean-op="and" id="location-dummy-rule-rule">
1481 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr" operation="ne" value="node1"/>
1482 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr-1" operation="ne" value="node2"/>
1483 	        </rule>
1484 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node3"/>
1485 	    </rule>
1486 	</rsc_location>
1487 	            """,
1488 	        )
1489 	        self.assertExpressionXml(
1490 	            [
1491 	                "#uname",
1492 	                "eq",
1493 	                "node1",
1494 	                "or",
1495 	                "#uname",
1496 	                "eq",
1497 	                "node2",
1498 	                "and",
1499 	                "#uname",
1500 	                "ne",
1501 	                "node3",
1502 	            ],
1503 	            """
1504 	<rsc_location id="location-dummy">
1505 	    <rule boolean-op="and" id="location-dummy-rule">
1506 	        <rule boolean-op="or" id="location-dummy-rule-rule">
1507 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr" operation="eq" value="node1"/>
1508 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr-1" operation="eq" value="node2"/>
1509 	        </rule>
1510 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node3"/>
1511 	    </rule>
1512 	</rsc_location>
1513 	            """,
1514 	        )
1515 	        self.assertExpressionXml(
1516 	            ["defined", "pingd", "and", "pingd", "lte", "1"],
1517 	            """
1518 	<rsc_location id="location-dummy">
1519 	    <rule boolean-op="and" id="location-dummy-rule">
1520 	        <expression attribute="pingd" id="location-dummy-rule-expr" operation="defined"/>
1521 	        <expression attribute="pingd" id="location-dummy-rule-expr-1" operation="lte" value="1"/>
1522 	    </rule>
1523 	</rsc_location>
1524 	            """,
1525 	        )
1526 	        self.assertExpressionXml(
1527 	            ["pingd", "gt", "1", "or", "not_defined", "pingd"],
1528 	            """
1529 	<rsc_location id="location-dummy">
1530 	    <rule boolean-op="or" id="location-dummy-rule">
1531 	        <expression attribute="pingd" id="location-dummy-rule-expr" operation="gt" value="1"/>
1532 	        <expression attribute="pingd" id="location-dummy-rule-expr-1" operation="not_defined"/>
1533 	    </rule>
1534 	</rsc_location>
1535 	            """,
1536 	        )
1537 	
1538 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1539 	    def testAndOrExpressionDateSpec(self):
1540 	        self.assertExpressionXml(
1541 	            ["#uname", "ne", "node1", "and", "date-spec", "hours=1-12"],
1542 	            """
1543 	<rsc_location id="location-dummy">
1544 	    <rule boolean-op="and" id="location-dummy-rule">
1545 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node1"/>
1546 	        <date_expression id="location-dummy-rule-expr-1" operation="date_spec">
1547 	            <date_spec hours="1-12" id="location-dummy-rule-expr-1-datespec"/>
1548 	        </date_expression>
1549 	    </rule>
1550 	</rsc_location>
1551 	            """,
1552 	        )
1553 	        self.assertExpressionXml(
1554 	            ["date-spec", "monthdays=1-12", "or", "#uname", "ne", "node1"],
1555 	            """
1556 	<rsc_location id="location-dummy">
1557 	    <rule boolean-op="or" id="location-dummy-rule">
1558 	        <date_expression id="location-dummy-rule-expr" operation="date_spec">
1559 	            <date_spec id="location-dummy-rule-expr-datespec" monthdays="1-12"/>
1560 	        </date_expression>
1561 	        <expression attribute="#uname" id="location-dummy-rule-expr-1" operation="ne" value="node1"/>
1562 	    </rule>
1563 	</rsc_location>
1564 	            """,
1565 	        )
1566 	        self.assertExpressionXml(
1567 	            [
1568 	                "date-spec",
1569 	                "monthdays=1-10",
1570 	                "or",
1571 	                "date-spec",
1572 	                "monthdays=11-20",
1573 	            ],
1574 	            """
1575 	<rsc_location id="location-dummy">
1576 	    <rule boolean-op="or" id="location-dummy-rule">
1577 	        <date_expression id="location-dummy-rule-expr" operation="date_spec">
1578 	            <date_spec id="location-dummy-rule-expr-datespec" monthdays="1-10"/>
1579 	        </date_expression>
1580 	        <date_expression id="location-dummy-rule-expr-1" operation="date_spec">
1581 	            <date_spec id="location-dummy-rule-expr-1-datespec" monthdays="11-20"/>
1582 	        </date_expression>
1583 	    </rule>
1584 	</rsc_location>
1585 	            """,
1586 	        )
1587 	
1588 	    # already moved to pcs_test/tier0/lib/cib/rule/test_parsed_to_cib.py
1589 	    def testParenthesizedExpression(self):
1590 	        self.assertExpressionXml(
1591 	            [
1592 	                "(",
1593 	                "#uname",
1594 	                "ne",
1595 	                "node1",
1596 	                "and",
1597 	                "#uname",
1598 	                "ne",
1599 	                "node2",
1600 	                ")",
1601 	                "or",
1602 	                "#uname",
1603 	                "eq",
1604 	                "node3",
1605 	            ],
1606 	            """
1607 	<rsc_location id="location-dummy">
1608 	    <rule boolean-op="or" id="location-dummy-rule">
1609 	        <rule boolean-op="and" id="location-dummy-rule-rule">
1610 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr" operation="ne" value="node1"/>
1611 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr-1" operation="ne" value="node2"/>
1612 	        </rule>
1613 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node3"/>
1614 	    </rule>
1615 	</rsc_location>
1616 	            """,
1617 	        )
1618 	        self.assertExpressionXml(
1619 	            [
1620 	                "#uname",
1621 	                "ne",
1622 	                "node1",
1623 	                "and",
1624 	                "(",
1625 	                "#uname",
1626 	                "ne",
1627 	                "node2",
1628 	                "or",
1629 	                "#uname",
1630 	                "eq",
1631 	                "node3",
1632 	                ")",
1633 	            ],
1634 	            """
1635 	<rsc_location id="location-dummy">
1636 	    <rule boolean-op="and" id="location-dummy-rule">
1637 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node1"/>
1638 	        <rule boolean-op="or" id="location-dummy-rule-rule">
1639 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr" operation="ne" value="node2"/>
1640 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr-1" operation="eq" value="node3"/>
1641 	        </rule>
1642 	    </rule>
1643 	</rsc_location>
1644 	            """,
1645 	        )
1646 	        self.assertExpressionXml(
1647 	            [
1648 	                "(",
1649 	                "#uname",
1650 	                "ne",
1651 	                "node1",
1652 	                "and",
1653 	                "#uname",
1654 	                "ne",
1655 	                "node2",
1656 	                ")",
1657 	                "or",
1658 	                "(",
1659 	                "#uname",
1660 	                "ne",
1661 	                "node3",
1662 	                "and",
1663 	                "#uname",
1664 	                "ne",
1665 	                "node4",
1666 	                ")",
1667 	            ],
1668 	            """
1669 	<rsc_location id="location-dummy">
1670 	    <rule boolean-op="or" id="location-dummy-rule">
1671 	        <rule boolean-op="and" id="location-dummy-rule-rule">
1672 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr" operation="ne" value="node1"/>
1673 	            <expression attribute="#uname" id="location-dummy-rule-rule-expr-1" operation="ne" value="node2"/>
1674 	        </rule>
1675 	        <rule boolean-op="and" id="location-dummy-rule-rule-1">
1676 	            <expression attribute="#uname" id="location-dummy-rule-rule-1-expr" operation="ne" value="node3"/>
1677 	            <expression attribute="#uname" id="location-dummy-rule-rule-1-expr-1" operation="ne" value="node4"/>
1678 	        </rule>
1679 	    </rule>
1680 	</rsc_location>
1681 	            """,
1682 	        )
1683 	        self.assertExpressionXml(
1684 	            [
1685 	                "(",
1686 	                "#uname",
1687 	                "ne",
1688 	                "node1",
1689 	                "and",
1690 	                "#uname",
1691 	                "ne",
1692 	                "node2",
1693 	                ")",
1694 	                "and",
1695 	                "(",
1696 	                "#uname",
1697 	                "ne",
1698 	                "node3",
1699 	                "and",
1700 	                "#uname",
1701 	                "ne",
1702 	                "node4",
1703 	                ")",
1704 	            ],
1705 	            """
1706 	<rsc_location id="location-dummy">
1707 	    <rule boolean-op="and" id="location-dummy-rule">
1708 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="ne" value="node1"/>
1709 	        <expression attribute="#uname" id="location-dummy-rule-expr-1" operation="ne" value="node2"/>
1710 	        <expression attribute="#uname" id="location-dummy-rule-expr-2" operation="ne" value="node3"/>
1711 	        <expression attribute="#uname" id="location-dummy-rule-expr-3" operation="ne" value="node4"/>
1712 	    </rule>
1713 	</rsc_location>
1714 	            """,
1715 	        )
1716 	
1717 	    @staticmethod
1718 	    def assertExpressionXml(rule_expression, rule_xml, cib_file=None):
1719 	        cib_dom = xml.dom.minidom.parse(
1720 	            cib_file if cib_file is not None else empty_cib
1721 	        )
1722 	        constraints = cib_dom.getElementsByTagName("constraints")[0]
1723 	        constraint_el = constraints.appendChild(
1724 	            cib_dom.createElement("rsc_location")
1725 	        )
1726 	        constraint_el.setAttribute("id", "location-dummy")
1727 	        assert_xml_equal(
1728 	            rule.CibBuilder(utils.getValidateWithVersion(cib_dom))
1729 	            .build(constraint_el, rule.RuleParser().parse(rule_expression))
1730 	            .parentNode.toprettyxml(indent="    "),
1731 	            rule_xml.lstrip().rstrip(" "),
1732 	        )
1733 	
1734 	
1735 	class TokenPreprocessorTest(TestCase):
1736 	    def setUp(self):
1737 	        self.preprocessor = rule.TokenPreprocessor()
1738 	
1739 	    def testNoChanges(self):
1740 	        self.assertEqual([], self.preprocessor.run([]))
1741 	
1742 	        self.assertEqual(
1743 	            ["#uname", "eq", "node1"],
1744 	            self.preprocessor.run(["#uname", "eq", "node1"]),
1745 	        )
1746 	
1747 	    def testDateSpec(self):
1748 	        self.assertEqual(["date-spec"], self.preprocessor.run(["date-spec"]))
1749 	
1750 	        self.assertEqual(
1751 	            ["date-spec", "hours=14"],
1752 	            self.preprocessor.run(["date-spec", "hours=14"]),
1753 	        )
1754 	
1755 	        self.assertEqual(
1756 	            ["date-spec", "hours weeks=6 months= moon=1"],
1757 	            self.preprocessor.run(
1758 	                ["date-spec", "hours", "weeks=6", "months=", "moon=1"]
1759 	            ),
1760 	        )
1761 	
1762 	        self.assertEqual(
1763 	            ["date-spec", "foo", "hours=14"],
1764 	            self.preprocessor.run(["date-spec", "foo", "hours=14"]),
1765 	        )
1766 	
1767 	        self.assertEqual(
1768 	            ["date-spec", "hours=14", "foo", "hours=14"],
1769 	            self.preprocessor.run(["date-spec", "hours=14", "foo", "hours=14"]),
1770 	        )
1771 	
1772 	        self.assertEqual(
1773 	            [
1774 	                "date-spec",
1775 	                "hours=1 monthdays=2 weekdays=3 yeardays=4 months=5 "
1776 	                "weeks=6 years=7 weekyears=8 moon=9",
1777 	            ],
1778 	            self.preprocessor.run(
1779 	                [
1780 	                    "date-spec",
1781 	                    "hours=1",
1782 	                    "monthdays=2",
1783 	                    "weekdays=3",
1784 	                    "yeardays=4",
1785 	                    "months=5",
1786 	                    "weeks=6",
1787 	                    "years=7",
1788 	                    "weekyears=8",
1789 	                    "moon=9",
1790 	                ]
1791 	            ),
1792 	        )
1793 	
1794 	        self.assertEqual(
1795 	            ["#uname", "eq", "node1", "or", "date-spec", "hours=14"],
1796 	            self.preprocessor.run(
1797 	                ["#uname", "eq", "node1", "or", "date-spec", "hours=14"]
1798 	            ),
1799 	        )
1800 	
1801 	        self.assertEqual(
1802 	            ["date-spec", "hours=14", "or", "#uname", "eq", "node1"],
1803 	            self.preprocessor.run(
1804 	                [
1805 	                    "date-spec",
1806 	                    "hours=14",
1807 	                    "or",
1808 	                    "#uname",
1809 	                    "eq",
1810 	                    "node1",
1811 	                ]
1812 	            ),
1813 	        )
1814 	
1815 	    def testDuration(self):
1816 	        self.assertEqual(["duration"], self.preprocessor.run(["duration"]))
1817 	
1818 	        self.assertEqual(
1819 	            ["duration", "hours=14"],
1820 	            self.preprocessor.run(["duration", "hours=14"]),
1821 	        )
1822 	
1823 	        self.assertEqual(
1824 	            ["duration", "hours weeks=6 months= moon=1"],
1825 	            self.preprocessor.run(
1826 	                ["duration", "hours", "weeks=6", "months=", "moon=1"]
1827 	            ),
1828 	        )
1829 	
1830 	        self.assertEqual(
1831 	            ["duration", "foo", "hours=14"],
1832 	            self.preprocessor.run(["duration", "foo", "hours=14"]),
1833 	        )
1834 	
1835 	        self.assertEqual(
1836 	            ["duration", "hours=14", "foo", "hours=14"],
1837 	            self.preprocessor.run(["duration", "hours=14", "foo", "hours=14"]),
1838 	        )
1839 	
1840 	        self.assertEqual(
1841 	            [
1842 	                "duration",
1843 	                "hours=1 monthdays=2 weekdays=3 yeardays=4 months=5 "
1844 	                "weeks=6 years=7 weekyears=8 moon=9",
1845 	            ],
1846 	            self.preprocessor.run(
1847 	                [
1848 	                    "duration",
1849 	                    "hours=1",
1850 	                    "monthdays=2",
1851 	                    "weekdays=3",
1852 	                    "yeardays=4",
1853 	                    "months=5",
1854 	                    "weeks=6",
1855 	                    "years=7",
1856 	                    "weekyears=8",
1857 	                    "moon=9",
1858 	                ]
1859 	            ),
1860 	        )
1861 	
1862 	        self.assertEqual(
1863 	            ["#uname", "eq", "node1", "or", "duration", "hours=14"],
1864 	            self.preprocessor.run(
1865 	                ["#uname", "eq", "node1", "or", "duration", "hours=14"]
1866 	            ),
1867 	        )
1868 	
1869 	        self.assertEqual(
1870 	            ["duration", "hours=14", "or", "#uname", "eq", "node1"],
1871 	            self.preprocessor.run(
1872 	                [
1873 	                    "duration",
1874 	                    "hours=14",
1875 	                    "or",
1876 	                    "#uname",
1877 	                    "eq",
1878 	                    "node1",
1879 	                ]
1880 	            ),
1881 	        )
1882 	
1883 	    def testParenthesis(self):
1884 	        self.assertEqual(["("], self.preprocessor.run(["("]))
1885 	
1886 	        self.assertEqual([")"], self.preprocessor.run([")"]))
1887 	
1888 	        self.assertEqual(
1889 	            ["(", "(", ")", ")"], self.preprocessor.run(["(", "(", ")", ")"])
1890 	        )
1891 	
1892 	        self.assertEqual(["(", "(", ")", ")"], self.preprocessor.run(["(())"]))
1893 	
1894 	        self.assertEqual(
1895 	            ["a", "(", "b", ")", "c"],
1896 	            self.preprocessor.run(["a", "(", "b", ")", "c"]),
1897 	        )
1898 	
1899 	        self.assertEqual(
1900 	            ["a", "(", "b", "c", ")", "d"],
1901 	            self.preprocessor.run(["a", "(", "b", "c", ")", "d"]),
1902 	        )
1903 	
1904 	        self.assertEqual(
1905 	            ["a", ")", "b", "(", "c"],
1906 	            self.preprocessor.run(["a", ")", "b", "(", "c"]),
1907 	        )
1908 	
1909 	        self.assertEqual(
1910 	            ["a", "(", "b", ")", "c"], self.preprocessor.run(["a", "(b)", "c"])
1911 	        )
1912 	
1913 	        self.assertEqual(
1914 	            ["a", "(", "b", ")", "c"], self.preprocessor.run(["a(", "b", ")c"])
1915 	        )
1916 	
1917 	        self.assertEqual(
1918 	            ["a", "(", "b", ")", "c"], self.preprocessor.run(["a(b)c"])
1919 	        )
1920 	
1921 	        self.assertEqual(
1922 	            ["aA", "(", "bB", ")", "cC"], self.preprocessor.run(["aA(bB)cC"])
1923 	        )
1924 	
1925 	        self.assertEqual(
1926 	            ["(", "aA", "(", "bB", ")", "cC", ")"],
1927 	            self.preprocessor.run(["(aA(bB)cC)"]),
1928 	        )
1929 	
1930 	        self.assertEqual(
1931 	            ["(", "aA", "(", "(", "bB", ")", "cC", ")"],
1932 	            self.preprocessor.run(["(aA(", "(bB)cC)"]),
1933 	        )
1934 	
1935 	        self.assertEqual(
1936 	            ["(", "aA", "(", "(", "(", "bB", ")", "cC", ")"],
1937 	            self.preprocessor.run(["(aA(", "(", "(bB)cC)"]),
1938 	        )
1939 	
1940 	
1941 	class ExportAsExpressionTest(TestCase):
1942 	    def test_success1(self):
1943 	        self.assertXmlExport(
1944 	            """
1945 	            <rule id="location-dummy-rule" score="INFINITY">
1946 	                <expression attribute="#uname" id="location-dummy-rule-expr"
1947 	                    operation="eq" value="node1"/>
1948 	            </rule>
1949 	            """,
1950 	            "#uname eq node1",
1951 	            "#uname eq string node1",
1952 	        )
1953 	
1954 	    def test_success2(self):
1955 	        self.assertXmlExport(
1956 	            """
1957 	            <rule id="location-dummy-rule" score="INFINITY">
1958 	                <expression attribute="foo" id="location-dummy-rule-expr"
1959 	                    operation="gt" type="version" value="1.2.3"/>
1960 	            </rule>
1961 	            """,
1962 	            "foo gt version 1.2.3",
1963 	            "foo gt version 1.2.3",
1964 	        )
1965 	
1966 	    def test_success3(self):
1967 	        self.assertXmlExport(
1968 	            """
1969 	<rule boolean-op="or" id="complexRule" score="INFINITY">
1970 	    <rule boolean-op="and" id="complexRule-rule-1" score="0">
1971 	        <date_expression id="complexRule-rule-1-expr" operation="date_spec">
1972 	            <date_spec id="complexRule-rule-1-expr-datespec" weekdays="1-5" hours="12-23"/>
1973 	        </date_expression>
1974 	        <date_expression id="complexRule-rule-1-expr-1" operation="in_range" start="2014-07-26">
1975 	            <duration id="complexRule-rule-1-expr-1-duration" months="1"/>
1976 	        </date_expression>
1977 	    </rule>
1978 	    <rule boolean-op="and" id="complexRule-rule" score="0">
1979 	        <expression attribute="foo" id="complexRule-rule-expr-1" operation="gt" type="version" value="1.2"/>
1980 	        <expression attribute="#uname" id="complexRule-rule-expr" operation="eq" value="node3 4"/>
1981 	    </rule>
1982 	</rule>
1983 	            """,
1984 	            '(date-spec hours=12-23 weekdays=1-5 and date in_range 2014-07-26 to duration months=1) or (foo gt version 1.2 and #uname eq "node3 4")',
1985 	            '(#uname eq string "node3 4" and foo gt version 1.2) or (date in_range 2014-07-26 to duration months=1 and date-spec hours=12-23 weekdays=1-5)',
1986 	        )
1987 	
1988 	    def test_success_integer(self):
1989 	        self.assertXmlExport(
1990 	            """
1991 	            <rule id="location-dummy-rule" score="INFINITY">
1992 	                <expression attribute="foo" id="location-dummy-rule-expr"
1993 	                    operation="gt" type="integer" value="123"/>
1994 	            </rule>
1995 	            """,
1996 	            "foo gt integer 123",
1997 	            "foo gt integer 123",
1998 	        )
1999 	
2000 	    def test_success_number(self):
2001 	        self.assertXmlExport(
2002 	            """
2003 	            <rule id="location-dummy-rule" score="INFINITY">
2004 	                <expression attribute="foo" id="location-dummy-rule-expr"
2005 	                    operation="gt" type="number" value="123"/>
2006 	            </rule>
2007 	            """,
2008 	            "foo gt number 123",
2009 	            "foo gt number 123",
2010 	        )
2011 	
2012 	    @staticmethod
2013 	    def assertXmlExport(rule_xml, export, export_normalized):
2014 	        ac(
2015 	            export + "\n",
2016 	            rule.ExportAsExpression().get_string(
2017 	                xml.dom.minidom.parseString(rule_xml).documentElement,
2018 	                normalize=False,
2019 	            )
2020 	            + "\n",
2021 	        )
2022 	        ac(
2023 	            export_normalized + "\n",
2024 	            rule.ExportAsExpression().get_string(
2025 	                xml.dom.minidom.parseString(rule_xml).documentElement,
2026 	                normalize=True,
2027 	            )
2028 	            + "\n",
2029 	        )
2030 	
2031 	
2032 	class DomRuleAddXmlTest(TestCase):
2033 	    def test_success_xml(self):
2034 	        self.assertExpressionXml(
2035 	            ["#uname", "eq", "node1"],
2036 	            """
2037 	<rsc_location id="location-dummy">
2038 	    <rule id="location-dummy-rule" score="INFINITY">
2039 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2040 	    </rule>
2041 	</rsc_location>
2042 	            """,
2043 	        )
2044 	        self.assertExpressionXml(
2045 	            ["id=myRule", "#uname", "eq", "node1"],
2046 	            """
2047 	<rsc_location id="location-dummy">
2048 	    <rule id="myRule" score="INFINITY">
2049 	        <expression attribute="#uname" id="myRule-expr" operation="eq" value="node1"/>
2050 	    </rule>
2051 	</rsc_location>
2052 	            """,
2053 	        )
2054 	        self.assertExpressionXml(
2055 	            ["score=INFINITY", "#uname", "eq", "node1"],
2056 	            """
2057 	<rsc_location id="location-dummy">
2058 	    <rule id="location-dummy-rule" score="INFINITY">
2059 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2060 	    </rule>
2061 	</rsc_location>
2062 	            """,
2063 	        )
2064 	        self.assertExpressionXml(
2065 	            ["score=100", "#uname", "eq", "node1"],
2066 	            """
2067 	<rsc_location id="location-dummy">
2068 	    <rule id="location-dummy-rule" score="100">
2069 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2070 	    </rule>
2071 	</rsc_location>
2072 	            """,
2073 	        )
2074 	        self.assertExpressionXml(
2075 	            ["score-attribute=pingd", "#uname", "eq", "node1"],
2076 	            """
2077 	<rsc_location id="location-dummy">
2078 	    <rule id="location-dummy-rule" score-attribute="pingd">
2079 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2080 	    </rule>
2081 	</rsc_location>
2082 	            """,
2083 	        )
2084 	        self.assertExpressionXml(
2085 	            [f"role={const.PCMK_ROLE_PROMOTED}", "#uname", "eq", "node1"],
2086 	            f"""
2087 	<rsc_location id="location-dummy">
2088 	    <rule id="location-dummy-rule" role="{const.PCMK_ROLE_PROMOTED}" score="INFINITY">
2089 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2090 	    </rule>
2091 	</rsc_location>
2092 	            """,
2093 	            cib_file=empty_cib_new_roles_supported,
2094 	        )
2095 	        self.assertExpressionXml(
2096 	            [f"role={const.PCMK_ROLE_PROMOTED}", "#uname", "eq", "node1"],
2097 	            f"""
2098 	<rsc_location id="location-dummy">
2099 	    <rule id="location-dummy-rule" role="{const.PCMK_ROLE_PROMOTED_LEGACY}" score="INFINITY">
2100 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2101 	    </rule>
2102 	</rsc_location>
2103 	            """,
2104 	        )
2105 	        self.assertExpressionXml(
2106 	            [
2107 	                f"role={str(const.PCMK_ROLE_UNPROMOTED).lower()}",
2108 	                "#uname",
2109 	                "eq",
2110 	                "node1",
2111 	            ],
2112 	            f"""
2113 	<rsc_location id="location-dummy">
2114 	    <rule id="location-dummy-rule" role="{const.PCMK_ROLE_UNPROMOTED_LEGACY}" score="INFINITY">
2115 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2116 	    </rule>
2117 	</rsc_location>
2118 	            """,
2119 	        )
2120 	        self.assertExpressionXml(
2121 	            [
2122 	                f"role={str(const.PCMK_ROLE_UNPROMOTED).lower()}",
2123 	                "#uname",
2124 	                "eq",
2125 	                "node1",
2126 	            ],
2127 	            f"""
2128 	<rsc_location id="location-dummy">
2129 	    <rule id="location-dummy-rule" role="{const.PCMK_ROLE_UNPROMOTED}" score="INFINITY">
2130 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" value="node1"/>
2131 	    </rule>
2132 	</rsc_location>
2133 	            """,
2134 	            cib_file=empty_cib_new_roles_supported,
2135 	        )
2136 	        self.assertExpressionXml(
2137 	            [
2138 	                "score=100",
2139 	                "id=myRule",
2140 	                f"role={str(const.PCMK_ROLE_PROMOTED).lower()}",
2141 	                "#uname",
2142 	                "eq",
2143 	                "node1",
2144 	            ],
2145 	            f"""
2146 	<rsc_location id="location-dummy">
2147 	    <rule id="myRule" role="{const.PCMK_ROLE_PROMOTED}" score="100">
2148 	        <expression attribute="#uname" id="myRule-expr" operation="eq" value="node1"/>
2149 	    </rule>
2150 	</rsc_location>
2151 	            """,
2152 	            cib_file=empty_cib_new_roles_supported,
2153 	        )
2154 	        self.assertExpressionXml(
2155 	            [
2156 	                "score=100",
2157 	                "id=myRule",
2158 	                f"role={str(const.PCMK_ROLE_PROMOTED).lower()}",
2159 	                "#uname",
2160 	                "eq",
2161 	                "node1",
2162 	            ],
2163 	            f"""
2164 	<rsc_location id="location-dummy">
2165 	    <rule id="myRule" role="{const.PCMK_ROLE_PROMOTED_LEGACY}" score="100">
2166 	        <expression attribute="#uname" id="myRule-expr" operation="eq" value="node1"/>
2167 	    </rule>
2168 	</rsc_location>
2169 	            """,
2170 	        )
2171 	        self.assertExpressionXml(
2172 	            ["#uname", "eq", "integer", "12345"],
2173 	            """
2174 	<rsc_location id="location-dummy">
2175 	    <rule id="location-dummy-rule" score="INFINITY">
2176 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="number" value="12345"/>
2177 	    </rule>
2178 	</rsc_location>
2179 	            """,
2180 	        )
2181 	        self.assertExpressionXml(
2182 	            ["#uname", "eq", "integer", "12345"],
2183 	            """
2184 	<rsc_location id="location-dummy">
2185 	    <rule id="location-dummy-rule" score="INFINITY">
2186 	        <expression attribute="#uname" id="location-dummy-rule-expr" operation="eq" type="integer" value="12345"/>
2187 	    </rule>
2188 	</rsc_location>
2189 	            """,
2190 	            cib_file=rc("cib-empty-3.5.xml"),
2191 	        )
2192 	
2193 	    @staticmethod
2194 	    def assertExpressionXml(rule_expression, rule_xml, cib_file=None):
(1) Event Sigma main event: The application uses Python's built in `xml` module which does not properly handle erroneous or maliciously constructed data, making the application vulnerable to one or more types of XML attacks.
(2) Event remediation: Avoid using the `xml` module. Consider using the `defusedxml` module or similar which safely prevents all XML entity attacks.
2195 	        cib_dom = xml.dom.minidom.parse(
2196 	            cib_file if cib_file is not None else empty_cib
2197 	        )
2198 	        constraints = cib_dom.getElementsByTagName("constraints")[0]
2199 	        constraint_el = constraints.appendChild(
2200 	            cib_dom.createElement("rsc_location")
2201 	        )
2202 	        constraint_el.setAttribute("id", "location-dummy")
2203 	        options, rule_argv = rule.parse_argv(rule_expression)
2204 	        rule.dom_rule_add(
2205 	            constraint_el,
2206 	            options,
2207 	            rule_argv,
2208 	            utils.getValidateWithVersion(cib_dom),
2209 	        )
2210 	        assert_xml_equal(
2211 	            constraint_el.toprettyxml(indent="    "),
2212 	            rule_xml.lstrip().rstrip(" "),
2213 	        )
2214 	
2215 	
2216 	class DomRuleAddTest(TestCase, AssertPcsMixin):
2217 	    def setUp(self):
2218 	        self.temp_cib = get_tmp_file("tier1_rule_dom_rule_add")
2219 	        write_file_to_tmpfile(empty_cib, self.temp_cib)
2220 	        self.pcs_runner = PcsRunner(self.temp_cib.name)
2221 	        self.pcs_runner.mock_settings = get_mock_settings()
2222 	        self.assert_pcs_success(
2223 	            "resource create dummy1 ocf:pcsmock:minimal".split(),
2224 	        )
2225 	
2226 	    def tearDown(self):
2227 	        self.temp_cib.close()
2228 	
2229 	    @skip_unless_crm_rule()
2230 	    def test_success(self):
2231 	        self.assert_pcs_success(
2232 	            "constraint location dummy1 rule #uname eq node1".split(),
2233 	        )
2234 	
2235 	        self.assert_pcs_success(
2236 	            (
2237 	                "constraint location dummy1 rule id=MyRule score=100 role="
2238 	                "{role} #uname eq node2"
2239 	            )
2240 	            .format(role=str(const.PCMK_ROLE_PROMOTED).lower())
2241 	            .split(),
2242 	        )
2243 	
2244 	        self.assert_pcs_success(
2245 	            (
2246 	                "constraint location dummy1 rule id=complexRule (#uname eq node3 "
2247 	                "and foo gt version 1.2) or (date-spec hours=12-23 weekdays=1-5 "
2248 	                "and date in_range 2014-07-26 to duration months=1)"
2249 	            ).split(),
2250 	        )
2251 	
2252 	        self.assert_pcs_success(
2253 	            (
2254 	                "constraint location dummy1 rule (#uname eq node3) and "
2255 	                "(date gt 2022-01-01 or date lt 2023-01-01 "
2256 	                "or date in_range 2023-02-01 to 2023-02-28)"
2257 	            ).split(),
2258 	        )
2259 	
2260 	        self.assert_pcs_success(
2261 	            "constraint location config --full".split(),
2262 	            dedent(
2263 	                f"""\
2264 	                Location Constraints:
2265 	                  resource 'dummy1' (id: location-dummy1)
2266 	                    Rules:
2267 	                      Rule: score=INFINITY (id: location-dummy1-rule)
2268 	                        Expression: #uname eq node1 (id: location-dummy1-rule-expr)
2269 	                  resource 'dummy1' (id: location-dummy1-1)
2270 	                    Rules:
2271 	                      Rule: role={const.PCMK_ROLE_PROMOTED_PRIMARY} score=100 (id: MyRule)
2272 	                        Expression: #uname eq node2 (id: MyRule-expr)
2273 	                  resource 'dummy1' (id: location-dummy1-2)
2274 	                    Rules:
2275 	                      Rule: boolean-op=or score=INFINITY (id: complexRule)
2276 	                        Rule: boolean-op=and score=0 (id: complexRule-rule)
2277 	                          Expression: #uname eq node3 (id: complexRule-rule-expr)
2278 	                          Expression: foo gt version 1.2 (id: complexRule-rule-expr-1)
2279 	                        Rule: boolean-op=and score=0 (id: complexRule-rule-1)
2280 	                          Expression: (id: complexRule-rule-1-expr)
2281 	                            Date Spec: hours=12-23 weekdays=1-5 (id: complexRule-rule-1-expr-datespec)
2282 	                          Expression: date in_range 2014-07-26 to duration (id: complexRule-rule-1-expr-1)
2283 	                            Duration: months=1 (id: complexRule-rule-1-expr-1-duration)
2284 	                  resource 'dummy1' (id: location-dummy1-3)
2285 	                    Rules:
2286 	                      Rule: boolean-op=and score=INFINITY (id: location-dummy1-3-rule)
2287 	                        Expression: #uname eq node3 (id: location-dummy1-3-rule-expr)
2288 	                        Rule: boolean-op=or score=0 (id: location-dummy1-3-rule-rule)
2289 	                          Expression: date gt 2022-01-01 (id: location-dummy1-3-rule-rule-expr)
2290 	                          Expression: date lt 2023-01-01 (id: location-dummy1-3-rule-rule-expr-1)
2291 	                          Expression: date in_range 2023-02-01 to 2023-02-28 (id: location-dummy1-3-rule-rule-expr-2)
2292 	                """,
2293 	            ),
2294 	        )
2295 	
2296 	        self.assert_pcs_success(
2297 	            "constraint location config".split(),
2298 	            dedent(
2299 	                f"""\
2300 	                Location Constraints:
2301 	                  resource 'dummy1'
2302 	                    Rules:
2303 	                      Rule: score=INFINITY
2304 	                        Expression: #uname eq node1
2305 	                  resource 'dummy1'
2306 	                    Rules:
2307 	                      Rule: role={const.PCMK_ROLE_PROMOTED_PRIMARY} score=100
2308 	                        Expression: #uname eq node2
2309 	                  resource 'dummy1'
2310 	                    Rules:
2311 	                      Rule: boolean-op=or score=INFINITY
2312 	                        Rule: boolean-op=and score=0
2313 	                          Expression: #uname eq node3
2314 	                          Expression: foo gt version 1.2
2315 	                        Rule: boolean-op=and score=0
2316 	                          Expression:
2317 	                            Date Spec: hours=12-23 weekdays=1-5
2318 	                          Expression: date in_range 2014-07-26 to duration
2319 	                            Duration: months=1
2320 	                  resource 'dummy1'
2321 	                    Rules:
2322 	                      Rule: boolean-op=and score=INFINITY
2323 	                        Expression: #uname eq node3
2324 	                        Rule: boolean-op=or score=0
2325 	                          Expression: date gt 2022-01-01
2326 	                          Expression: date lt 2023-01-01
2327 	                          Expression: date in_range 2023-02-01 to 2023-02-28
2328 	                """,
2329 	            ),
2330 	        )
2331 	
2332 	    @skip_unless_crm_rule()
2333 	    def test_invalid_score(self):
2334 	        self.assert_pcs_success(
2335 	            "constraint location dummy1 rule score-attribute=pingd defined pingd".split(),
2336 	        )
2337 	
2338 	        self.assert_pcs_success(
2339 	            "constraint location config --full".split(),
2340 	            dedent(
2341 	                """\
2342 	                Location Constraints:
2343 	                  resource 'dummy1' (id: location-dummy1)
2344 	                    Rules:
2345 	                      Rule: score-attribute=pingd (id: location-dummy1-rule)
2346 	                        Expression: defined pingd (id: location-dummy1-rule-expr)
2347 	                """,
2348 	            ),
2349 	        )
2350 	
2351 	    def test_invalid_rule(self):
2352 	        self.assert_pcs_fail(
2353 	            "constraint location dummy1 rule score=100".split(),
2354 	            "Error: no rule expression was specified\n",
2355 	        )
2356 	
2357 	        self.assert_pcs_fail(
2358 	            "constraint location dummy1 rule #uname eq".split(),
2359 	            "Error: '#uname eq' is not a valid rule expression: unexpected end "
2360 	            "of rule\n",
2361 	        )
2362 	
2363 	        self.assert_pcs_fail(
2364 	            "constraint location dummy1 rule string #uname eq node1".split(),
2365 	            "Error: 'string #uname eq node1' is not a valid rule expression: "
2366 	            "unexpected 'string' before 'eq'\n",
2367 	        )
2368 	
2369 	    @skip_unless_crm_rule()
2370 	    def test_invalid_options(self):
2371 	        self.assert_pcs_fail(
2372 	            "constraint location dummy1 rule role=foo #uname eq node1".split(),
2373 	            "Error: invalid role 'foo', use {}\n".format(
2374 	                format_list_custom_last_separator(
2375 	                    const.PCMK_ROLES_PROMOTED + const.PCMK_ROLES_UNPROMOTED,
2376 	                    " or ",
2377 	                )
2378 	            ),
2379 	        )
2380 	
2381 	        self.assert_pcs_fail(
2382 	            (
2383 	                "constraint location dummy1 rule score=100 score-attribute=pingd "
2384 	                "#uname eq node1"
2385 	            ).split(),
2386 	            "Error: can not specify both score and score-attribute\n",
2387 	        )
2388 	
2389 	        self.assert_pcs_fail(
2390 	            "constraint location dummy1 rule id=1foo #uname eq node1".split(),
2391 	            "Error: invalid rule id '1foo', '1' is not a valid first character "
2392 	            "for a rule id\n",
2393 	        )
2394 	
2395 	        self.assert_pcs_success("constraint location config --full".split(), "")
2396 	
2397 	        self.assert_pcs_success(
2398 	            "constraint location dummy1 rule id=MyRule #uname eq node1".split(),
2399 	        )
2400 	
2401 	        self.assert_pcs_success(
2402 	            "constraint location config --full".split(),
2403 	            dedent(
2404 	                """\
2405 	                Location Constraints:
2406 	                  resource 'dummy1' (id: location-dummy1)
2407 	                    Rules:
2408 	                      Rule: score=INFINITY (id: MyRule)
2409 	                        Expression: #uname eq node1 (id: MyRule-expr)
2410 	                """,
2411 	            ),
2412 	        )
2413 	
2414 	        self.assert_pcs_fail(
2415 	            "constraint location dummy1 rule id=MyRule #uname eq node1".split(),
2416 	            "Error: id 'MyRule' is already in use, please specify another one\n",
2417 	        )
2418 	
2419 	    @skip_unless_crm_rule()
2420 	    def test_invalid_date(self):
2421 	        self.assert_pcs_fail(
2422 	            "constraint location dummy1 rule date gt abcd".split(),
2423 	            (
2424 	                "Error: 'date gt abcd' is not a valid rule expression: 'abcd' "
2425 	                "is not an ISO 8601 date\n"
2426 	            ),
2427 	        )
2428 	        self.assert_pcs_fail(
2429 	            "constraint location dummy1 rule date in_range abcd to 2023-01-01".split(),
2430 	            (
2431 	                "Error: 'date in_range abcd to 2023-01-01' is not a valid rule "
2432 	                "expression: invalid date 'abcd' in 'in_range ... to'\n"
2433 	            ),
2434 	        )
2435 	        self.assert_pcs_fail(
2436 	            "constraint location dummy1 rule date in_range 2023-01-01 to abcd".split(),
2437 	            (
2438 	                "Error: 'date in_range 2023-01-01 to abcd' is not a valid rule "
2439 	                "expression: invalid date 'abcd' in 'in_range ... to'\n"
2440 	            ),
2441 	        )
2442