Skip to content

Commit 6ce8e96

Browse files
authored
Dev: ui_corosync: Improve corosync.show and corosync.edit subcommands (#1264)
- corosync.show: validate corosync.conf before showing it - corosync.edit: edit a temporary copy of corosync.conf, validate it before saving it, then give the user hints on how to sync to other nodes - Add corosync_ui.feature to collect test cases for crm corosync ui
2 parents f016a26 + aa17646 commit 6ce8e96

File tree

6 files changed

+54
-28
lines changed

6 files changed

+54
-28
lines changed

.github/workflows/crmsh-ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,22 @@ jobs:
165165
token: ${{ secrets.CODECOV_TOKEN }}
166166
flags: integration
167167

168+
functional_test_corosync_ui:
169+
runs-on: ubuntu-24.04
170+
timeout-minutes: 40
171+
steps:
172+
- uses: actions/checkout@v4
173+
- name: functional test for crm corosync subcommand
174+
run: |
175+
echo '{ "exec-opts": ["native.cgroupdriver=systemd"] }' | sudo tee /etc/docker/daemon.json
176+
sudo systemctl restart docker.service
177+
index=`$GET_INDEX_OF corosync_ui`
178+
$DOCKER_SCRIPT $index
179+
- uses: codecov/codecov-action@v4
180+
with:
181+
token: ${{ secrets.CODECOV_TOKEN }}
182+
flags: integration
183+
168184
functional_test_bootstrap_options_non_root:
169185
runs-on: ubuntu-24.04
170186
timeout-minutes: 40

codecov.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ coverage:
88
threshold: 0.35%
99
codecov:
1010
notify:
11-
after_n_builds: 26
11+
after_n_builds: 27
1212
comment:
13-
after_n_builds: 26
13+
after_n_builds: 27
1414
layout: "condensed_header, flags, files, condensed_footer"
1515

1616
ignore:

crmsh/ui_corosync.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,21 @@ def do_edit(self, context):
121121
'''
122122
cfg = corosync.conf()
123123
try:
124-
utils.edit_file_ext(cfg, template='')
124+
rc = utils.edit_file_ext(cfg, corosync.is_valid_corosync_conf)
125+
if rc and len(utils.list_cluster_nodes()) > 1:
126+
logger.warning(f"\"{cfg}\" has changed, should be synced with other nodes")
127+
logger.info("Use \"crm corosync diff\" to show the difference")
128+
logger.info("Use \"crm corosync push\" to sync")
125129
except IOError as e:
126130
context.fatal_error(str(e))
127131

128132
def do_show(self, context):
129133
'''
130134
Display the corosync configuration.
131135
'''
132-
cfg = corosync.conf()
133-
if not os.path.isfile(cfg):
134-
context.fatal_error("No corosync configuration found on this node.")
135-
utils.page_string(open(cfg).read())
136+
if not corosync.is_valid_corosync_conf():
137+
return False
138+
utils.page_file(corosync.conf())
136139

137140
def do_log(self, context):
138141
'''

crmsh/utils.py

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,31 +1230,27 @@ def edit_file(fname):
12301230
return ext_cmd_nosudo("%s %s" % (config.core.editor, fname))
12311231

12321232

1233-
def edit_file_ext(fname, template=''):
1233+
def edit_file_ext(fname: str, validator: typing.Callable[[typing.IO], bool] = None) -> bool:
12341234
'''
12351235
Edit a file via a temporary file.
12361236
Raises IOError on any error.
1237+
1238+
returns True if the file was changed
12371239
'''
1238-
if not os.path.isfile(fname):
1239-
s = template
1240-
else:
1241-
s = open(fname).read()
1242-
filehash = hash(s)
1243-
tmpfile = str2tmp(s)
1244-
try:
1245-
try:
1246-
if edit_file(tmpfile) != 0:
1247-
return
1248-
s = open(tmpfile, 'r').read()
1249-
if hash(s) == filehash: # file unchanged
1250-
return
1251-
f2 = open(fname, 'w')
1252-
f2.write(s)
1253-
f2.close()
1254-
finally:
1255-
os.unlink(tmpfile)
1256-
except OSError as e:
1257-
raise IOError(e)
1240+
with create_tempfile() as tmpfile:
1241+
shutil.copyfile(fname, tmpfile)
1242+
if edit_file(tmpfile) != 0:
1243+
raise IOError(f"Cannot edit file \"{fname}\"")
1244+
changed_data = read_from_file(tmpfile)
1245+
source_data = read_from_file(fname)
1246+
if changed_data != source_data:
1247+
if validator and not validator(tmpfile):
1248+
return False
1249+
# The original file needs to be replaced atomically
1250+
str2file(changed_data, fname)
1251+
return True
1252+
else:
1253+
return False
12581254

12591255

12601256
def need_pager(s, w, h):

data-manifest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ test/features/bootstrap_sbd_normal.feature
7373
test/features/cluster_api.feature
7474
test/features/configure_bugs.feature
7575
test/features/constraints_bugs.feature
76+
test/features/corosync_ui.feature
7677
test/features/coveragerc
7778
test/features/crm_report_bugs.feature
7879
test/features/crm_report_normal.feature

test/features/corosync_ui.feature

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@bootstrap
2+
Feature: crm corosync ui test cases
3+
4+
Need nodes: hanode1 hanode2
5+
6+
Scenario: Empty cluster
7+
When Try "crm corosync show" on "hanode1"
8+
Then Except "No such file or directory: '/etc/corosync/corosync.conf'" in stderr
9+
When Try "crm corosync set totem.cluster_name xin" on "hanode1"
10+
Then Except "No such file or directory: '/etc/corosync/corosync.conf'" in stderr

0 commit comments

Comments
 (0)