Permalink
Browse files

Adds user-selectable hash merging support in vars

Hash variables are currently overriden if they are redefined. This
doesn't let the user refine hash entries or overriding selected keys,
which can, for some, be a desirable feature.
This patch let the user force hash merging by setting the
hash_behaviour value to "merge" (without the quotes) in ansible.cfg

However, by default, ansible behaves like it always did and if any value
besides "merge" is used ("replace" is suggested in the example ansible.cfg
file), it will also behave as always.
  • Loading branch information...
leucos committed Jan 23, 2013
1 parent 51180fa commit e28e538c6ed7520ecef305c776eb6036aff42d06
View
@@ -76,6 +76,16 @@ remote_port=22
sudo_exe=sudo
# how to handle hash defined in several places
# hash can be merged, or replaced
# if you use replace, and have multiple hashes named 'x', the last defined
# will override the previously defined one
# if you use merge here, hash will cumulate their keys, but keys will still
# override each other
# replace is the default value, and is how ansible always handled hash variables
#
# hash_behaviour=replace
# if set, always use this private key file for authentication, same as if passing
# --private-key to ansible or ansible-playbook
View
@@ -92,6 +92,7 @@ def shell_expand_path(path):
DEFAULT_SYSLOG_FACILITY = get_config(p, DEFAULTS, 'syslog_facility', 'ANSIBLE_SYSLOG_FACILITY', 'LOG_USER')
DEFAULT_KEEP_REMOTE_FILES = get_config(p, DEFAULTS, 'keep_remote_files', 'ANSIBLE_KEEP_REMOTE_FILES', '0')
DEFAULT_SUDO_EXE = get_config(p, DEFAULTS, 'sudo_exe', 'ANSIBLE_SUDO_EXE', 'sudo')
DEFAULT_HASH_BEHAVIOUR = get_config(p, DEFAULTS, 'hash_behaviour', 'ANSIBLE_HASH_BEHAVIOUR', 'replace')
DEFAULT_ACTION_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'action_plugins', None, '/usr/share/ansible_plugins/action_plugins'))
DEFAULT_CALLBACK_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'callback_plugins', None, '/usr/share/ansible_plugins/callback_plugins'))
@@ -19,6 +19,7 @@
import glob
from ansible import errors
from ansible import utils
import ansible.constants as C
class VarsModule(object):
@@ -48,15 +49,22 @@ def run(self, host):
data = utils.parse_yaml_from_file(path)
if type(data) != dict:
raise errors.AnsibleError("%s must be stored as a dictionary/hash" % path)
results.update(data)
if C.DEFAULT_HASH_BEHAVIOUR == "merge":
# let data content override results if needed
results = utils.merge_hash(results, data)
else:
results.update(data)
# load vars in playbook_dir/group_vars/name_of_host
path = os.path.join(basedir, "host_vars/%s" % host.name)
if os.path.exists(path):
data = utils.parse_yaml_from_file(path)
if type(data) != dict:
raise errors.AnsibleError("%s must be stored as a dictionary/hash" % path)
results.update(data)
if C.DEFAULT_HASH_BEHAVIOUR == "merge":
# let data content override results if needed
results = utils.merge_hash(results, data)
else:
results.update(data)
return results
@@ -19,6 +19,7 @@
import os
import shlex
import yaml
import copy
import optparse
import operator
from ansible import errors
@@ -273,6 +274,28 @@ def parse_kv(args):
options[k]=v
return options
def merge_hash(a, b):
''' merges hash b into a
this means that if b has key k, the resulting has will have a key k
which value comes from b
said differently, all key/value combination from b will override a's '''
# let's create a deep copy of a
result = copy.deepcopy(a)
# and iterate over b keys
for k, v in b.iteritems():
if k in result and isinstance(result[k], dict):
# if this key is a hash and exists in a
# we recursively call ourselves with
# the key value of b
result[k] = merge_hash(result[k], v)
else:
# k is not in a, no need to merge b, we just deecopy
# or k is not a dictionnary, no need to merge b either, we just deecopy it
result[k] = copy.deepcopy(v)
# finally, return the resulting hash when we're done iterating keys
return result
def md5s(data):
''' Return MD5 hex digest of data. '''

0 comments on commit e28e538

Please sign in to comment.