Skip to content

Commit d12f04c

Browse files
authored
[crmsh-4.5] Dev: ui_resource: Refine 'do_failcount' function (#1626)
backport #1624
2 parents 05da45b + de23591 commit d12f04c

File tree

3 files changed

+75
-40
lines changed

3 files changed

+75
-40
lines changed

crmsh/ui_resource.py

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Copyright (C) 2013-2018 Kristoffer Gronlund <kgronlund@suse.com>
33
# See COPYING for license information.
44

5+
import re
56
from . import command
67
from . import completers as compl
78
from . import constants
@@ -172,7 +173,8 @@ def is_resource(obj):
172173
return True
173174

174175

175-
_attrcmds = compl.choice(['delete', 'set', 'show'])
176+
ATTR_OPERATION_CMDS = ('delete', 'set', 'show')
177+
_attrcmds = compl.choice(ATTR_OPERATION_CMDS)
176178
_raoperations = compl.choice(constants.ra_operations)
177179

178180

@@ -490,20 +492,23 @@ def do_failcount(self, context, rsc, cmd, node, value=None, operation=None, inte
490492
failcount <rsc> delete <node>
491493
failcount <rsc> show <node>"""
492494
def sec_to_ms(s):
493-
return s + '000'
495+
return '0' if s == '0' else s + '000'
494496

495497
def ms_to_sec(m):
496-
return m[:len(m)-3]
498+
return '0' if m == '0' else str(int(m) // 1000)
499+
500+
USAGE = "Usage: failcount <rsc> set <node> <value> [operation] [interval]"
497501

498502
if rsc not in compl.resources():
499-
context.warning("Resource {} not exists in this cluster".format(rsc))
503+
logger.warning("Resource %s not in this cluster", rsc)
500504
return
501-
valid_cmd_list = ["set", "delete", "show"]
502-
if cmd not in valid_cmd_list:
503-
context.fatal_error("{} is not valid command(should be one of {})".format(cmd, valid_cmd_list))
505+
if cmd not in ATTR_OPERATION_CMDS:
506+
context.fatal_error(f"{cmd} is not valid command(should be one of {', '.join(ATTR_OPERATION_CMDS)})")
504507
nodeid = utils.get_nodeid_from_name(node)
505508
if nodeid is None:
506-
context.fatal_error("Node {} not in this cluster".format(node))
509+
context.fatal_error(f"Node {node} not in this cluster")
510+
if value and (not value.isdigit() and value != "INFINITY"):
511+
context.fatal_error("value should be a non-negative integer or 'INFINITY'")
507512

508513
if cmd == "set":
509514
# query the current failcount status
@@ -513,46 +518,50 @@ def ms_to_sec(m):
513518
context.fatal_error(err)
514519

515520
# try to get failcount dict {operation:interval}
516-
import re
517-
failcount_res = re.findall(r'fail-count-{}#(.*)_([0-9]+)'.format(rsc), out)
518-
if not failcount_res:
519-
return True if value and int(value) == 0 else False
520-
failcount_dict = dict(failcount_res)
521-
522-
# validate for operation and interval
523-
if operation and operation not in failcount_dict.keys():
524-
context.fatal_error("Usage: failcount <rsc> set <node> <value> [operation] [interval]\n\
525-
Should specify operation between \"{}\"".format(' '.join(failcount_dict.keys())))
526-
if (operation and interval) and (operation, sec_to_ms(interval)) not in failcount_res:
527-
context.fatal_error("Usage: failcount <rsc> set <node> <value> [operation] [interval]\n\
528-
Should specify (operation, interval) between {}".\
529-
format([(op, ms_to_sec(inter)) for op, inter in failcount_res]))
521+
failcount_dict = dict(re.findall(rf'fail-count-{rsc}#(.*)_([0-9]+)', out))
522+
if failcount_dict:
523+
# validate for operation and interval
524+
if operation and operation not in failcount_dict:
525+
context.fatal_error(f"Should specify operation between ({', '.join(failcount_dict.keys())})\n{USAGE}")
526+
if (operation and interval) and (operation, sec_to_ms(interval)) not in failcount_dict.items():
527+
failcount_items = [(op, ms_to_sec(inter)) for op, inter in failcount_dict.items()]
528+
context.fatal_error(f"Should specify (operation, interval) between {failcount_items}\n{USAGE}")
529+
else:
530+
# no failcount entry for this resource and node
531+
if operation and interval:
532+
rsc = f'{rsc}#{operation}_{sec_to_ms(interval)}'
533+
else:
534+
# when no failcount entry, it's no harm to give a warning and return true
535+
# https://bugzilla.suse.com/show_bug.cgi?id=1180332#c11
536+
logger.warning("Should specify operation and interval\n%s", USAGE)
537+
return
530538

531539
# just one failcount entry
532-
if len(failcount_res) == 1:
533-
operation = failcount_res[0][0]
534-
interval = failcount_dict[operation]
535-
rsc = '{}#{}_{}'.format(rsc, operation, interval)
540+
if len(failcount_dict) == 1:
541+
operation, interval_in_ms = failcount_dict.popitem()
542+
rsc = f'{rsc}#{operation}_{interval_in_ms}'
536543

537544
# multiple failcount entries for this resource and node
538-
if len(failcount_res) > 1:
545+
if len(failcount_dict) > 1:
539546
if operation and interval:
540-
rsc = '{}#{}_{}'.format(rsc, operation, sec_to_ms(interval))
547+
rsc = f'{rsc}#{operation}_{sec_to_ms(interval)}'
541548
elif int(value) == 0:
542-
# using '-P' option of 'crm_attribute' command
549+
op_interval_list = [
550+
f'{op}_{inter}'
551+
for op, inter in failcount_dict.items()
552+
if not operation or op == operation
553+
]
554+
op_interval_str = '|'.join(op_interval_list)
555+
rsc = f'{rsc}#{op_interval_str}'
556+
# using '-P' option of 'crm_attribute' command, to match the pattern
543557
cmd = "set_p"
544-
if operation:
545-
op_interval_str = '|'.join(['{}_{}'.format(operation, inter) for op, inter in failcount_res if op==operation])
546-
else:
547-
op_interval_str = '|'.join(['{}_{}'.format(op, inter) for op, inter in failcount_res])
548-
rsc = '{}#({})'.format(rsc, op_interval_str)
549558
else:
550559
# value != 0
551-
if operation and len([op for op, _ in failcount_res if op == operation]) == 1:
552-
rsc = '{}#{}_{}'.format(rsc, operation, failcount_dict[operation])
560+
if operation and len([op for op in failcount_dict if op == operation]) == 1:
561+
rsc = f'{rsc}#{operation}_{failcount_dict[operation]}'
553562
else:
554-
context.fatal_error("Should specify (operation, interval) between {}".
555-
format([(op, ms_to_sec(inter)) for op, inter in failcount_res]))
563+
failcount_items = [(op, ms_to_sec(inter)) for op, inter in failcount_dict.items()]
564+
context.fatal_error(f"Should specify (operation, interval) between {failcount_items}\n{USAGE}")
556565

557566
return ui_utils.manage_attr(context.get_command_name(), self.rsc_failcount,
558567
rsc, cmd, node, value)

doc/crm.8.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,9 @@ failcount <rsc> show <node>
17141714
...............
17151715
Example:
17161716
...............
1717-
failcount fs_0 delete node2
1717+
failcount fs_0 set node1 0 monitor 10
1718+
failcount fs_0 delete node1
1719+
failcount fs_0 show node1
17181720
...............
17191721

17201722
[[cmdhelp_resource_locate,show the location of resources]]

test/features/resource_failcount.feature

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,33 @@ Feature: Use "crm resource failcount" to manage failcounts
1313

1414
@clean
1515
Scenario: Validation, input the wrong parameters
16+
When Try "crm resource failcount ddd show hanode1"
17+
Then Except "Resource ddd not in this cluster"
1618
When Try "crm resource failcount d showss hanode1"
17-
Then Except "ERROR: resource.failcount: showss is not valid command(should be one of ['set', 'delete', 'show'])"
19+
Then Except "ERROR: resource.failcount: showss is not valid command(should be one of delete, set, show)"
1820
When Try "crm resource failcount d set hanode11 0"
1921
Then Except "ERROR: resource.failcount: Node hanode11 not in this cluster"
22+
When Try "crm resource failcount d set hanode1 12s"
23+
Then Except "ERROR: resource.failcount: value should be a non-negative integer or 'INFINITY'"
2024

2125
@clean
2226
Scenario: Set the failcount to 0
2327
When Run "rm -f /run/resource-agents/Dummy-d.state" on "hanode1"
2428
And Wait "5" seconds
2529
Then Resource "d" failcount on "hanode1" is "1"
30+
When Try "crm resource failcount d set hanode1 0 start 0"
31+
Then Except "ERROR: resource.failcount: Should specify operation between (monitor)"
32+
When Try "crm resource failcount d set hanode1 0 monitor 5"
33+
Then Except "ERROR: resource.failcount: Should specify (operation, interval) between [('monitor', '3')]"
2634
When Run "crm resource failcount d set hanode1 0" on "hanode1"
2735
Then Resource "d" failcount on "hanode1" is "0"
36+
# no failcount entry
37+
When Run "crm configure primitive d2 Dummy op monitor interval=10s" on "hanode1"
38+
Then Resource "d2" type "Dummy" is "Started"
39+
When Try "crm resource failcount d2 set hanode1 0"
40+
Then Except "WARNING: Should specify operation and interval"
41+
When Run "crm resource failcount d2 set hanode1 0 monitor 10" on "hanode1"
42+
Then Resource "d2" failcount on "hanode1" is "0"
2843

2944
@clean
3045
Scenario: Set multiple failcounts to 0
@@ -35,6 +50,11 @@ Feature: Use "crm resource failcount" to manage failcounts
3550
"""
3651
now have two failcount entries, one is monitor, another is stop
3752
"""
53+
When Run "crm resource failcount d set hanode1 100 stop 0" on "hanode1"
54+
"""
55+
set stop failcounts to 100, specify both operation and interval
56+
"""
57+
Then Resource "d" failcount on "hanode1" is "101"
3858
When Run "crm resource failcount d set hanode1 0" on "hanode1"
3959
"""
4060
set all failcounts to 0
@@ -58,4 +78,8 @@ Feature: Use "crm resource failcount" to manage failcounts
5878
set monitor failcounts to 0
5979
"""
6080
Then Resource "d" failcount on "hanode1" is "0"
81+
When Try "crm resource failcount d set hanode1 1"
82+
Then Except "ERROR: resource.failcount: Should specify (operation, interval) between [('monitor', '3'), ('stop', '0')]"
83+
When Run "crm resource failcount d set hanode1 1 stop" on "hanode1"
84+
Then Resource "d" failcount on "hanode1" is "1"
6185

0 commit comments

Comments
 (0)