import ansible.inventory from ansiblereview import Result, Error from ansible.errors import AnsibleError import inspect import os try: import ansible.parsing.dataloader from ansible.vars.manager import VariableManager ANSIBLE = 2 except ImportError: try: from ansible.vars.manager import VariableManager ANSIBLE = 2 except ImportError: ANSIBLE = 1 _vars = dict() _inv = None def get_group_vars(group, inventory): try: from ansible.inventory.helpers import get_group_vars return get_group_vars(inventory.groups.values()) except ImportError: pass # http://stackoverflow.com/a/197053 vars = inspect.getargspec(inventory.get_group_vars) if 'return_results' in vars[0]: return inventory.get_group_vars(group, return_results=True) else: return inventory.get_group_vars(group) def remove_inherited_and_overridden_vars(vars, group, inventory): if group not in _vars: _vars[group] = get_group_vars(group, inventory) gv = _vars[group] for (k, v) in vars.items(): if k in gv: if gv[k] == v: vars.pop(k) else: gv.pop(k) def remove_inherited_and_overridden_group_vars(group, inventory): if group not in _vars: _vars[group] = get_group_vars(group, inventory) for ancestor in group.get_ancestors(): remove_inherited_and_overridden_vars(_vars[group], ancestor, inventory) def same_variable_defined_in_competing_groups(candidate, options): result = Result(candidate.path) # assume that group_vars file is under an inventory *directory* invfile = os.path.dirname(os.path.dirname(candidate.path)) global _inv try: if ANSIBLE > 1: loader = ansible.parsing.dataloader.DataLoader() try: from ansible.inventory.manager import InventoryManager inv = _inv or InventoryManager(loader=loader, sources=invfile) except ImportError: var_manager = VariableManager() inv = _inv or ansible.inventory.Inventory(loader=loader, variable_manager=var_manager, host_list=invfile) _inv = inv else: inv = _inv or ansible.inventory.Inventory(invfile) _inv = inv except AnsibleError as e: result.errors = [Error(None, "Inventory is broken: %s" % e.message)] return result if hasattr(inv, 'groups'): group = inv.groups.get(os.path.basename(candidate.path)) else: group = inv.get_group(os.path.basename(candidate.path)) if not group: # group file exists in group_vars but no related group # in inventory directory return result remove_inherited_and_overridden_group_vars(group, inv) group_vars = set(_vars[group].keys()) child_hosts = group.hosts child_groups = group.child_groups siblings = set() for child_host in child_hosts: siblings.update(child_host.groups) for child_group in child_groups: siblings.update(child_group.parent_groups) for sibling in siblings: if sibling != group: remove_inherited_and_overridden_group_vars(sibling, inv) sibling_vars = set(_vars[sibling].keys()) common_vars = sibling_vars & group_vars common_hosts = [host.name for host in set(child_hosts) & set(sibling.hosts)] if common_vars and common_hosts: for var in common_vars: error_msg_template = "Sibling groups {0} and {1} with common hosts {2} " + \ "both define variable {3}" error_msg = error_msg_template.format(group.name, sibling.name, ", ".join(common_hosts), var) result.errors.append(Error(None, error_msg)) return result