Skip to content

Commit ec25d36

Browse files
authored
Merge pull request #1415 from liangxin1300/20240507_corosync_parser_improve
corosync config parser minor changes
2 parents 8f4bef4 + 258d56e commit ec25d36

File tree

3 files changed

+46
-10
lines changed

3 files changed

+46
-10
lines changed

crmsh/corosync.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
COROSYNC_TOKEN_DEFAULT = 1000 # in ms units
2525
COROSYNC_CONF_TEMPLATE = """
26+
# Generated by crmsh
27+
# For more details please see corosync.conf.5 man page
2628
totem {
2729
version: 2
2830
}

crmsh/corosync_config_format.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88

99

1010
logger = logging.getLogger(__name__)
11+
COMMENT_PREFIX = '#comment'
1112

1213

1314
class Parser:
1415
"""SAX-style parser for the configuration of corosync"""
1516

16-
_SPLIT_RE = re.compile('\\s*({|:|})\\s*')
17+
_SPLIT_RE = re.compile('\\s*(#|{|:|})\\s*')
1718

1819
def __init__(self, ifile):
1920
"""Parse data form a file-like object."""
@@ -34,14 +35,15 @@ def _parse(self, ifile):
3435
line = line.strip()
3536
if not line:
3637
continue
37-
if line[0] == '#':
38-
continue
3938
try:
4039
tokens = self._tokenize(line)
4140
except ValueError as e:
4241
raise MalformedLineException(lineno, line)
4342
logger.debug('tokens: %s', tokens)
44-
if tokens[1] == '{':
43+
if tokens[1] == '#':
44+
fake_key = f'{COMMENT_PREFIX}_{lineno}'
45+
self.on_key_value(fake_key, tokens[2])
46+
elif tokens[1] == '{':
4547
if not tokens[0] or tokens[2]:
4648
raise MalformedLineException(lineno, line)
4749
self.__section_stack.append(tokens[0])
@@ -231,15 +233,18 @@ def on_dict(self, node):
231233
self.on_dict(value)
232234
del self._path_stack[-1]
233235
self.__write_indent(len(self._path_stack))
234-
self._ofile.write('}\n')
236+
self._ofile.write('}\n\n')
235237
case list(_):
236238
self._path_stack.append(key)
237239
self.on_list(value)
238240
del self._path_stack[-1]
239241
case _:
240242
self.__write_indent(len(self._path_stack))
241-
self._ofile.write(key)
242-
self._ofile.write(': ')
243+
if key.startswith(COMMENT_PREFIX):
244+
self._ofile.write('# ')
245+
else:
246+
self._ofile.write(key)
247+
self._ofile.write(': ')
243248
self.on_value(value)
244249
self._ofile.write('\n')
245250

@@ -253,7 +258,7 @@ def on_list(self, node):
253258
self._ofile.write(' {\n')
254259
self.on_dict(item)
255260
self.__write_indent(len(self._path_stack) - 1)
256-
self._ofile.write('}\n')
261+
self._ofile.write('}\n\n')
257262
case list(_):
258263
raise ValueError('list of list is invalid')
259264
case _:

test/unittests/test_corosync_config_format.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,24 @@ def test_unbalanced_brace(self):
5151

5252
class TestDomParserWithUnusedFeature(TestCase):
5353
def test_list_of_scalar(self):
54+
# list of scalar is not used in corosync.conf
5455
dom = DomParser(StringIO('''
55-
# list of scalar is not used in corosync.conf
5656
foo: a
5757
foo: b
5858
foo: c
5959
''')).dom()
6060
self.assertDictEqual({'foo': ['a', 'b', 'c']}, dom)
6161

6262

63+
class TestDomParserWithComment(TestCase):
64+
def test_with_comment(self):
65+
dom = DomParser(StringIO('''
66+
# comment line one
67+
# comment line two
68+
''')).dom()
69+
self.assertDictEqual({'#comment_1': 'comment line one', '#comment_2': 'comment line two'}, dom)
70+
71+
6372
class TestDomQueryGet(TestCase):
6473
def setUp(self) -> None:
6574
self.dom = {'scalar': {'scalar': 'value'}, 'vector': [{'scalar': 'value1'}, {'scalar': 'value2'}]}
@@ -188,12 +197,15 @@ def test_serialize(self):
188197
'''scalar {
189198
\tscalar: value
190199
}
200+
191201
vector {
192202
\tscalar: value1
193203
}
204+
194205
vector {
195206
\tscalar: value2
196207
}
208+
197209
''',
198210

199211
buf.getvalue()
@@ -228,7 +240,9 @@ def test_serialize_list_of_scalar(self):
228240

229241
class TestParseSerialize(TestCase):
230242
def test_parse_serialize(self):
231-
data = '''totem {
243+
data = '''# Generated by crmsh
244+
# Do not edit this file directly
245+
totem {
232246
\tversion: 2
233247
\tcluster_name: capybara
234248
\ttransport: knet
@@ -240,19 +254,24 @@ def test_parse_serialize(self):
240254
\tcrypto_cipher: aes256
241255
\tinterface {
242256
\t\tlinknumber: 0
257+
\t\t# knet_options
243258
\t\tknet_transport: udp
244259
\t\tknet_link_priority: 0
245260
\t}
261+
246262
\tinterface {
247263
\t\tlinknumber: 1
248264
\t\tknet_transport: sctp
249265
\t}
266+
250267
\tinterface {
251268
\t\tlinknumber: 2
252269
\t\tknet_transport: udp
253270
\t\tknet_link_priority: 10
254271
\t}
272+
255273
}
274+
256275
nodelist {
257276
\tnode {
258277
\t\tring0_addr: 192.168.1.101
@@ -261,31 +280,38 @@ def test_parse_serialize(self):
261280
\t\tnodeid: 1
262281
\t\tname: mynode1
263282
\t}
283+
264284
\tnode {
265285
\t\tring0_addr: 192.168.1.102
266286
\t\tring1_addr: 192.168.10.2
267287
\t\tring2_addr: 192.168.9.2
268288
\t\tnodeid: 2
269289
\t\tname: mynode2
270290
\t}
291+
271292
\tnode {
272293
\t\tring0_addr: 192.168.1.103
273294
\t\tring1_addr: 192.168.10.3
274295
\t\tring2_addr: 192.168.9.3
275296
\t\tnodeid: 3
276297
\t\tname: mynode3
277298
\t}
299+
278300
\tnode {
279301
\t\tring0_addr: 192.168.1.104
280302
\t\tring1_addr: 192.168.10.4
281303
\t\tring2_addr: 192.168.9.4
282304
\t\tnodeid: 4
283305
\t\tname: mynode4
284306
\t}
307+
285308
}
309+
310+
# quorum section
286311
quorum {
287312
\tprovider: corosync_votequorum
288313
}
314+
289315
logging {
290316
\tto_logfile: yes
291317
\tlogfile: /var/log/cluster/corosync.log
@@ -294,8 +320,11 @@ def test_parse_serialize(self):
294320
\t\tsubsys: KNET
295321
\t\tdebug: on
296322
\t}
323+
297324
}
325+
298326
'''
327+
self.maxDiff = None
299328
buf = StringIO(data)
300329
dom = DomParser(buf).dom()
301330
buf = StringIO()

0 commit comments

Comments
 (0)