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