Skip to content

Commit

Permalink
Dev: crmshmod: Improve parse logic for corner cases
Browse files Browse the repository at this point in the history
* Parse multi interface/node as list in the dict
* When there is no space between words in corosync.conf, like "totem{" or "ring0_addr:10.10.10.121",
  corosync still works, but our parse results are wrong
  • Loading branch information
liangxin1300 committed Apr 2, 2021
1 parent 2bcc2e7 commit 87ddc98
Showing 1 changed file with 63 additions and 21 deletions.
84 changes: 63 additions & 21 deletions salt/states/crmshmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

# Import python libs
from __future__ import absolute_import, unicode_literals, print_function
import re


# Import salt libs
Expand Down Expand Up @@ -298,11 +299,16 @@ def cluster_configured(
return ret


def _convert2dict(file_content_lines):
DUMMY_SEC_NAME_TEMPL = "__{}_list"
KNOWN_SEC_NAMES_WITH_LIST = ("totem.interface", "nodelist.node")


def _convert2dict(file_content_lines, initial_path=""):
"""
Convert the corosync configuration file to a dictionary
"""
corodict = {}
sub_dict = {}
index = 0

for i, line in enumerate(file_content_lines):
Expand All @@ -313,61 +319,97 @@ def _convert2dict(file_content_lines):
if index > i:
continue

line_items = stripped_line.split()
if '{' in stripped_line:
corodict[line_items[0]], new_index = _convert2dict(file_content_lines[i+1:])
sec_name = re.sub("\s*{", "", stripped_line)
initial_path += ".{}".format(sec_name) if initial_path else sec_name
sub_dict, new_index = _convert2dict(file_content_lines[i+1:], initial_path)
if initial_path in KNOWN_SEC_NAMES_WITH_LIST:
dummpy_sec_name = DUMMY_SEC_NAME_TEMPL.format(sec_name)
if dummpy_sec_name not in corodict:
corodict[dummpy_sec_name] = []
corodict[dummpy_sec_name].append({sec_name: sub_dict})
else:
corodict[sec_name] = sub_dict
index = i + new_index
elif line_items[0][-1] == ':':
corodict[line_items[0][:-1]] = line_items[-1]
initial_path = re.sub("\.{}".format(sec_name), "", initial_path) if "." in initial_path else ""
elif ':' in stripped_line:
key, *values = stripped_line.split(':')
corodict[key] = ':'.join(values).strip()
elif '}' in stripped_line:
return corodict, i+2

return corodict, index


def _mergedicts(main_dict, changes_dict, applied_changes, initial_path=''):
def _mergedicts(main_dict, changes_dict, applied_changes, initial_path='', index=0):
"""
Merge the 2 dictionaries. We cannot use update as it changes all the children of an entry
"""
for key, value in changes_dict.items():
current_path = '{}.{}'.format(initial_path, key)
current_path = '{}.{}'.format(initial_path, key) if initial_path else key
if key in main_dict.keys() and not isinstance(value, dict):
if str(main_dict[key]) != str(value):
applied_changes[current_path] = value
main_dict[key] = value
elif key in main_dict.keys():
modified_dict, new_changes = _mergedicts(main_dict[key], value, applied_changes, current_path)
main_dict[key] = modified_dict
applied_changes.update(new_changes)

modified_dict, new_changes = _mergedicts(main_dict[key], value, applied_changes, current_path, index)
elif DUMMY_SEC_NAME_TEMPL.format(key) in main_dict.keys():
dummpy_sec_name = DUMMY_SEC_NAME_TEMPL.format(key)
if index < len(main_dict[dummpy_sec_name]):
modified_dict, new_changes = _mergedicts(main_dict[dummpy_sec_name][index][key], value, applied_changes, current_path, index)
else:
main_dict[dummpy_sec_name].append(changes_dict)
applied_changes[current_path] = value
else: # Entry not found in current main dictionary, so we can update all
main_dict[key] = changes_dict[key]
applied_changes[current_path] = value

return main_dict, applied_changes


def _unpack_list_in_dict(value_list, indentation):
"""
Convert dict list to string
"""
output = ''
for item_dict in value_list:
output += _convert2corosync(item_dict, indentation)
return output


def _unpack_dict(key, value, indentation):
"""
Convert dict to string
"""
output = ''
if isinstance(value, dict):
output += '{}{} {{\n'.format(indentation, key)
indentation += '\t'
output += _convert2corosync(value, indentation)
indentation = indentation[:-1]
output += '{}}}\n\n'.format(indentation)
elif isinstance(value, list):
output += _unpack_list_in_dict(value, indentation)
else:
output += '{}{}: {}\n'.format(indentation, key, value)
return output


def _convert2corosync(corodict, indentation=''):
"""
Convert a corosync data dictionary to the corosync configuration file format
"""
output = ''
for key, value in corodict.items():
if isinstance(value, dict):
output += '{}{} {{\n'.format(indentation, key)
indentation += '\t'
output += _convert2corosync(value, indentation)
indentation = indentation[:-1]
output += '{}}}\n'.format(indentation)
else:
output += '{}{}: {}\n'.format(indentation, key, value)
output += _unpack_dict(key, value, indentation)
return output


def corosync_updated(
name,
data,
backup=True):
backup=True,
index=0):
"""
Configure corosync configuration file
Expand All @@ -386,7 +428,7 @@ def corosync_updated(

with salt_utils.files.fopen(name, 'r') as file_content:
corodict, _ = _convert2dict(file_content.read().splitlines())
new_conf_dict, changes = _mergedicts(corodict, data, {})
new_conf_dict, changes = _mergedicts(corodict, data, {}, index=index)

if not changes:
ret['changes'] = changes
Expand Down

0 comments on commit 87ddc98

Please sign in to comment.