Recursively combine dictionaries
I think this does what you want:
def resolve(groups, hosts):
# Groups that have already been resolved
resolved_groups = {}
# Group names that are not root
non_root = set()
# Make dict with resolution of each group
result = {}
for name in groups:
result[name] = _resolve_rec(name, groups, hosts, resolved_groups, non_root)
# Remove groups that are not root
for name in groups:
if name in non_root:
del result[name]
return result
def _resolve_rec(name, groups, hosts, resolved_groups, non_root):
# If group has already been resolved finish
if name in resolved_groups:
return resolved_groups[name]
# If it is a host finish
if name in hosts:
return hosts[name]
# New group resolution
resolved = {}
for child in groups[name]:
# Resolve each child
resolved[child] = _resolve_rec(child, groups, hosts, resolved_groups, non_root)
# Mark child as non-root
non_root.add(child)
# Save to resolved groups
resolved_groups[name] = resolved
return resolved
With your example:
groups = {
'servers': ['unix_servers', 'windows_servers'],
'unix_servers': ['server_a', 'server_b', 'server_group'],
'windows_servers': ['server_c', 'server_d'],
'server_group': ['server_e', 'server_f']
}
hosts = {
'server_a': '10.0.0.1',
'server_b': '10.0.0.2',
'server_c': '10.0.0.3',
'server_d': '10.0.0.4',
'server_e': '10.0.0.5',
'server_f': '10.0.0.6'
}
d3 = {
'servers': {
'unix_servers': {
'server_a': '10.0.0.1',
'server_b': '10.0.0.2',
'server_group': {
'server_e': '10.0.0.5',
'server_f': '10.0.0.6'
}
},
'windows_servers': {
'server_c': '10.0.0.3',
'server_d': '10.0.0.4'
}
}
}
print(resolve(groups, hosts) == d3)
# True
Note this can fall into infinite recursion for malformed inputs, if you have for example group A
containing group B
but then group B
contains group A
.