def get_inherited_vars(self, dep_chain=[]): inherited_vars = dict() for parent in dep_chain: inherited_vars = combine_vars(inherited_vars, parent._role_vars) inherited_vars = combine_vars(inherited_vars, parent._role_params) return inherited_vars
def get_default_vars(self): # FIXME: get these from dependent roles too default_vars = dict() for dep in self.get_all_dependencies(): default_vars = combine_vars(default_vars, dep.get_default_vars()) default_vars = combine_vars(default_vars, self._default_vars) return default_vars
def get_inherited_vars(self): inherited_vars = dict() for parent in self._parents: inherited_vars = combine_vars(inherited_vars, parent.get_inherited_vars()) inherited_vars = combine_vars(inherited_vars, parent._role_vars) inherited_vars = combine_vars(inherited_vars, parent._role_params) return inherited_vars
def get_role_params(self, dep_chain=[]): params = {} if dep_chain: for parent in dep_chain: params = combine_vars(params, parent._role_params) params = combine_vars(params, self._role_params) return params
def _get_host_variables(self, hostname, vault_password=None): host = self.get_host(hostname) if host is None: raise errors.AnsibleError("host not found: %s" % hostname) vars = {} # plugin.run retrieves all vars (also from groups) for host vars_results = [ plugin.run(host, vault_password=vault_password) for plugin in self._vars_plugins if hasattr(plugin, 'run')] for updated in vars_results: if updated is not None: # FIXME: combine_vars vars = combine_vars(vars, updated) # plugin.get_host_vars retrieves just vars for specific host vars_results = [ plugin.get_host_vars(host, vault_password=vault_password) for plugin in self._vars_plugins if hasattr(plugin, 'get_host_vars')] for updated in vars_results: if updated is not None: # FIXME: combine_vars vars = combine_vars(vars, updated) # still need to check InventoryParser per host vars # which actually means InventoryScript per host, # which is not performant if self.parser is not None: # FIXME: combine_vars vars = combine_vars(vars, self.parser.get_host_variables(host)) # Read host_vars/ files # FIXME: combine_vars vars = combine_vars(vars, self.get_host_vars(host)) return vars
def boilerplate_module(modfile, args, interpreter, check, destfile): """ simulate what ansible does with new style modules """ loader = DataLoader() complex_args = {} if args.startswith("@"): # Argument is a YAML file (JSON is a subset of YAML) complex_args = utils_vars.combine_vars(complex_args, loader.load_from_file(args[1:])) args = '' elif args.startswith("{"): # Argument is a YAML document (not a file) complex_args = utils_vars.combine_vars(complex_args, loader.load(args)) args = '' if args: parsed_args = parse_kv(args) complex_args = utils_vars.combine_vars(complex_args, parsed_args) task_vars = {} if interpreter: if '=' not in interpreter: print("interpreter must by in the form of \ ansible_python_interpreter=/usr/bin/python") sys.exit(1) interpreter_type, interpreter_path = interpreter.split('=') if not interpreter_type.startswith('ansible_'): interpreter_type = 'ansible_%s' % interpreter_type if not interpreter_type.endswith('_interpreter'): interpreter_type = '%s_interpreter' % interpreter_type task_vars[interpreter_type] = interpreter_path if check: complex_args['_ansible_check_mode'] = True modname = os.path.basename(modfile) modname = os.path.splitext(modname)[0] (module_data, module_style, shebang) = module_common.modify_module( modname, modfile, complex_args, task_vars=task_vars ) if module_style == 'new' \ and 'ZIPLOADER_WRAPPER = True' in module_data: module_style = 'ziploader' modfile2_path = os.path.expanduser(destfile) print("* including generated source,\ if any, saving to: %s" % modfile2_path) if module_style not in ('ziploader', 'old'): print("* this may offset any line numbers in tracebacks/debuggers!") modfile2 = open(modfile2_path, 'w') modfile2.write(module_data) modfile2.close() modfile = modfile2_path return (modfile2_path, modname, module_style)
def _load_vars(self, attr, ds): ''' Vars in a play can be specified either as a dictionary directly, or as a list of dictionaries. If the later, this method will turn the list into a single dictionary. ''' def _validate_variable_keys(ds): for key in ds: if not isidentifier(key): raise TypeError("'%s' is not a valid variable name" % key) try: if isinstance(ds, dict): _validate_variable_keys(ds) return combine_vars(self.vars, ds) elif isinstance(ds, list): all_vars = self.vars for item in ds: if not isinstance(item, dict): raise ValueError _validate_variable_keys(item) all_vars = combine_vars(all_vars, item) return all_vars elif ds is None: return {} else: raise ValueError except ValueError as e: raise AnsibleParserError("Vars in a %s must be specified as a dictionary, or a list of dictionaries" % self.__class__.__name__, obj=ds, orig_exc=e) except TypeError as e: raise AnsibleParserError("Invalid variable name in vars specified for %s: %s" % (self.__class__.__name__, e), obj=ds, orig_exc=e)
def get_default_vars(self, dep_chain=[]): default_vars = dict() for dep in self.get_all_dependencies(): default_vars = combine_vars(default_vars, dep.get_default_vars()) if dep_chain: for parent in dep_chain: default_vars = combine_vars(default_vars, parent._default_vars) default_vars = combine_vars(default_vars, self._default_vars) return default_vars
def get_role_params(self, dep_chain=None): dep_chain = [] if dep_chain is None else dep_chain params = {} if dep_chain: for parent in dep_chain: params = combine_vars(params, parent._role_params) params = combine_vars(params, self._role_params) return params
def get_vars(self, dep_chain=[]): all_vars = self.get_inherited_vars(dep_chain) for dep in self.get_all_dependencies(): all_vars = combine_vars(all_vars, dep.get_vars()) all_vars = combine_vars(all_vars, self._role_vars) all_vars = combine_vars(all_vars, self._role_params) return all_vars
def plugins_by_groups(): ''' merges all plugin sources by group, This should be used instead, NOT in combination with the other groups_plugins* functions ''' data = {} for group in host_groups: data[group] = combine_vars(data[group], _plugins_inventory(group)) data[group] = combine_vars(data[group], _plugins_play(group)) return data
def parse_inventory(self, host_list): if isinstance(host_list, basestring): if "," in host_list: host_list = host_list.split(",") host_list = [h for h in host_list if h and h.strip()] if host_list is None: self.parser = None elif isinstance(host_list, list): self.parser = None all = Group("all") self.groups = [all] ipv6_re = re.compile("\[([a-f:A-F0-9]*[%[0-z]+]?)\](?::(\d+))?") for x in host_list: m = ipv6_re.match(x) if m: all.add_host(Host(m.groups()[0], m.groups()[1])) else: if ":" in x: tokens = x.rsplit(":", 1) # if there is ':' in the address, then this is an ipv6 if ":" in tokens[0]: all.add_host(Host(x)) else: all.add_host(Host(tokens[0], tokens[1])) else: all.add_host(Host(x)) elif self._loader.path_exists(host_list): # TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self._loader.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, filename=host_list) else: self.parser = get_file_parser(host_list, self._loader) vars_loader.add_directory(self.basedir(), with_subdir=True) if self.parser: self.groups = self.parser.groups.values() else: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) self._vars_plugins = [x for x in vars_loader.all(self)] # FIXME: shouldn't be required, since the group/host vars file # management will be done in VariableManager # get group vars from group_vars/ files and vars plugins for group in self.groups: group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) # get host vars from host_vars/ files and vars plugins for host in self.get_hosts(): host.vars = combine_vars(host.vars, self.get_host_variables(host.name))
def parse_inventory(self, host_list): if isinstance(host_list, string_types): if "," in host_list: host_list = host_list.split(",") host_list = [ h for h in host_list if h and h.strip() ] self.parser = None # Always create the 'all' and 'ungrouped' groups, even if host_list is # empty: in this case we will subsequently an the implicit 'localhost' to it. ungrouped = Group('ungrouped') all = Group('all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) if host_list is None: pass elif isinstance(host_list, list): for h in host_list: try: (host, port) = parse_address(h, allow_ranges=False) except AnsibleError as e: display.vvv("Unable to parse address from hostname, leaving unchanged: %s" % to_unicode(e)) host = h port = None all.add_host(Host(host, port)) elif self._loader.path_exists(host_list): #TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, groups=self.groups, filename=host_list) else: self.parser = get_file_parser(host_list, self.groups, self._loader) vars_loader.add_directory(self.basedir(), with_subdir=True) if not self.parser: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) else: display.warning("Host file not found: %s" % to_unicode(host_list)) self._vars_plugins = [ x for x in vars_loader.all(self) ] # set group vars from group_vars/ files and vars plugins for g in self.groups: group = self.groups[g] group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) # set host vars from host_vars/ files and vars plugins for host in self.get_hosts(): host.vars = combine_vars(host.vars, self.get_host_variables(host.name))
def get_vars(self): results = {} groups = self.get_groups() for group in sorted(groups, key=lambda g: g.depth): results = combine_vars(results, group.get_vars()) results = combine_vars(results, self.vars) results['inventory_hostname'] = self.name results['inventory_hostname_short'] = self.name.split('.')[0] results['group_names'] = sorted([ g.name for g in groups if g.name != 'all']) return results
def get_vars(self, dep_chain=[], include_params=True): all_vars = self.get_inherited_vars(dep_chain) for dep in self.get_all_dependencies(): all_vars = combine_vars(all_vars, dep.get_vars(include_params=include_params)) all_vars = combine_vars(all_vars, self._role_vars) if include_params: all_vars = combine_vars(all_vars, self.get_role_params(dep_chain=dep_chain)) return all_vars
def _get_hostgroup_vars(self, host=None, group=None, new_pb_basedir=False, return_results=False): """ Loads variables from group_vars/<groupname> and host_vars/<hostname> in directories parallel to the inventory base directory or in the same directory as the playbook. Variables in the playbook dir will win over the inventory dir if files are in both. """ results = {} scan_pass = 0 _basedir = self._basedir _playbook_basedir = self._playbook_basedir # look in both the inventory base directory and the playbook base directory # unless we do an update for a new playbook base dir if not new_pb_basedir and _playbook_basedir: basedirs = [_basedir, _playbook_basedir] else: basedirs = [_basedir] for basedir in basedirs: # this can happen from particular API usages, particularly if not run # from /usr/bin/ansible-playbook if basedir in ('', None): basedir = './' scan_pass = scan_pass + 1 # it's not an eror if the directory does not exist, keep moving if not os.path.exists(basedir): continue # save work of second scan if the directories are the same if _basedir == _playbook_basedir and scan_pass != 1: continue # Before trying to load vars from file, check that the directory contains relvant file names if host is None and any(map(lambda ext: group.name + ext in self._group_vars_files, C.YAML_FILENAME_EXTENSIONS)): # load vars in dir/group_vars/name_of_group base_path = to_text(os.path.abspath(os.path.join(to_bytes(basedir), b"group_vars/" + to_bytes(group.name))), errors='surrogate_or_strict') host_results = self._variable_manager.add_group_vars_file(base_path, self._loader) if return_results: results = combine_vars(results, host_results) elif group is None and any(map(lambda ext: host.name + ext in self._host_vars_files, C.YAML_FILENAME_EXTENSIONS)): # same for hostvars in dir/host_vars/name_of_host base_path = to_text(os.path.abspath(os.path.join(to_bytes(basedir), b"host_vars/" + to_bytes(host.name))), errors='surrogate_or_strict') group_results = self._variable_manager.add_host_vars_file(base_path, self._loader) if return_results: results = combine_vars(results, group_results) # all done, results is a dictionary of variables for this particular host. return results
def parse_inventory(self, host_list): if isinstance(host_list, string_types): if "," in host_list: host_list = host_list.split(",") host_list = [ h for h in host_list if h and h.strip() ] self.parser = None # Always create the 'all' and 'ungrouped' groups, even if host_list is # empty: in this case we will subsequently an the implicit 'localhost' to it. ungrouped = Group(name='ungrouped') all = Group('all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) if host_list is None: pass elif isinstance(host_list, list): for h in host_list: (host, port) = parse_address(h, allow_ranges=False) all.add_host(Host(host, port)) elif self._loader.path_exists(host_list): #TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins' if self._loader.is_directory(host_list): # Ensure basedir is inside the directory host_list = os.path.join(self.host_list, "") self.parser = InventoryDirectory(loader=self._loader, groups=self.groups, filename=host_list) else: self.parser = get_file_parser(host_list, self.groups, self._loader) vars_loader.add_directory(self.basedir(), with_subdir=True) if not self.parser: # should never happen, but JIC raise AnsibleError("Unable to parse %s as an inventory source" % host_list) self._vars_plugins = [ x for x in vars_loader.all(self) ] # FIXME: shouldn't be required, since the group/host vars file # management will be done in VariableManager # get group vars from group_vars/ files and vars plugins for group in self.groups.values(): group.vars = combine_vars(group.vars, self.get_group_variables(group.name)) # get host vars from host_vars/ files and vars plugins for host in self.get_hosts(): host.vars = combine_vars(host.vars, self.get_host_variables(host.name))
def load_inventory_files(self, inventory_files, host_list=None): temp_vars = self._variable_manager.get_vars(loader=self._loader) templar = Templar(loader=self._loader, variables=temp_vars) all_inventory = AbleMapping() try: for inventory_file in inventory_files: inventory_file = templar.template(inventory_file) try: data = InventoryYaml.prepare_data(self._loader.load_from_file(inventory_file)) if data is not None: if self._directory is None: self._directory = os.path.dirname(inventory_file) all_inventory = combine_vars(all_inventory, data) break except AbleFileNotFound as e: # we continue on loader failures continue except AbleParserError as e: raise else: raise AbleFileNotFound("vars file %s was not found" % inventory_files) except (UndefinedError, AbleUndefinedVariable): # we do not have a full context here, and the missing variable could be # because of that, so just show a warning and continue display.vvv("skipping inventory_file '%s' due to an undefined variable" % inventory_files) return all_inventory
def _load_role_yaml(self, subdir, main=None, allow_dir=False): file_path = os.path.join(self._role_path, subdir) if self._loader.path_exists(file_path) and self._loader.is_directory(file_path): # Valid extensions and ordering for roles is hard-coded to maintain # role portability extensions = ['.yml', '.yaml', '.json'] # If no <main> is specified by the user, look for files with # extensions before bare name. Otherwise, look for bare name first. if main is None: _main = 'main' extensions.append('') else: _main = main extensions.insert(0, '') found_files = self._loader.find_vars_files(file_path, _main, extensions, allow_dir) if found_files: data = {} for found in found_files: new_data = self._loader.load_from_file(found) if new_data and allow_dir: data = combine_vars(data, new_data) else: data = new_data return data elif main is not None: raise AnsibleParserError("Could not find specified file in role: %s/%s" % (subdir, main)) return None
def get_magic_vars(self): results = {} results['inventory_hostname'] = self.name results['inventory_hostname_short'] = self.name.split('.')[0] results['group_names'] = sorted([g.name for g in self.get_groups() if g.name != 'all']) return combine_vars(self.vars, results)
def set_host_overrides(self, host): self._host = host host_vars = combine_vars(host.get_group_vars(), host.get_vars()) self._nspawn_cmd = host_vars.get('nspawn_command', 'systemd-nspawn') self._nspawn_args = shlex.split(host_vars.get('nspawn_extra_args', '')) self._nspawn_sudo = boolean(host_vars.get('nspawn_sudo', 'false'))
def _get_plugin_vars(plugin, path, entities): data = {} try: data = plugin.get_vars(self.loader, path, entity) except AttributeError: try: if isinstance(entity, Host): data = combine_vars(data, plugin.get_host_vars(entity.name)) else: data = combine_vars(data, plugin.get_group_vars(entity.name)) except AttributeError: if hasattr(plugin, 'run'): raise AnsibleError("Cannot use v1 type vars plugin %s from %s" % (plugin._load_name, plugin._original_path)) else: raise AnsibleError("Invalid vars plugin %s from %s" % (plugin._load_name, plugin._original_path)) return data
def get_group_vars(groups): results = {} for group in sort_groups(groups): results = combine_vars(results, group.get_vars()) return results
def _set_connection_options(self, variables, templar): # Keep the pre-delegate values for these keys PRESERVE_ORIG = ('inventory_hostname',) # create copy with delegation built in final_vars = combine_vars(variables, variables.get('ansible_delegated_vars', dict()).get(self._task.delegate_to, dict())) # grab list of usable vars for this plugin option_vars = C.config.get_plugin_vars('connection', self._connection._load_name) # create dict of 'templated vars' options = {'_extras': {}} for k in option_vars: if k in PRESERVE_ORIG: options[k] = templar.template(variables[k]) elif k in final_vars: options[k] = templar.template(final_vars[k]) # add extras if plugin supports them if getattr(self._connection, 'allow_extras', False): for k in final_vars: if k.startswith('ansible_%s_' % self._connection._load_name) and k not in options: options['_extras'][k] = templar.template(final_vars[k]) # set options with 'templated vars' specific to this plugin self._connection.set_options(var_options=options) self._set_shell_options(final_vars, templar)
def _get_hostgroup_vars(self, host=None, group=None, new_pb_basedir=False): """ Loads variables from group_vars/<groupname> and host_vars/<hostname> in directories parallel to the inventory base directory or in the same directory as the playbook. Variables in the playbook dir will win over the inventory dir if files are in both. """ results = {} scan_pass = 0 _basedir = self.basedir() # look in both the inventory base directory and the playbook base directory # unless we do an update for a new playbook base dir if not new_pb_basedir: basedirs = [_basedir, self._playbook_basedir] else: basedirs = [self._playbook_basedir] for basedir in basedirs: # this can happen from particular API usages, particularly if not run # from /usr/bin/ansible-playbook if basedir in ('', None): basedir = './' scan_pass = scan_pass + 1 # it's not an eror if the directory does not exist, keep moving if not os.path.exists(basedir): continue # save work of second scan if the directories are the same if _basedir == self._playbook_basedir and scan_pass != 1: continue if group and host is None: # load vars in dir/group_vars/name_of_group base_path = to_unicode(os.path.abspath(os.path.join(to_bytes(basedir), b"group_vars/" + to_bytes(group.name))), errors='strict') results = combine_vars(results, self._variable_manager.add_group_vars_file(base_path, self._loader)) elif host and group is None: # same for hostvars in dir/host_vars/name_of_host base_path = to_unicode(os.path.abspath(os.path.join(to_bytes(basedir), b"host_vars/" + to_bytes(host.name))), errors='strict') results = combine_vars(results, self._variable_manager.add_host_vars_file(base_path, self._loader)) # all done, results is a dictionary of variables for this particular host. return results
def get_inherited_vars(self, dep_chain=None): dep_chain = [] if dep_chain is None else dep_chain inherited_vars = dict() if dep_chain: for parent in dep_chain: inherited_vars = combine_vars(inherited_vars, parent._role_vars) return inherited_vars
def _get_group_variables(self, groupname, vault_password=None): group = self.get_group(groupname) if group is None: raise Exception("group not found: %s" % groupname) vars = {} # plugin.get_group_vars retrieves just vars for specific group vars_results = [ plugin.get_group_vars(group, vault_password=vault_password) for plugin in self._vars_plugins if hasattr(plugin, 'get_group_vars')] for updated in vars_results: if updated is not None: vars = combine_vars(vars, updated) # Read group_vars/ files vars = combine_vars(vars, self.get_group_vars(group)) return vars
def set_playbook_basedir(self, dir): """ sets the base directory of the playbook so inventory can use it as a basedir for host_ and group_vars, and other things. """ # Only update things if dir is a different playbook basedir if dir != self._playbook_basedir: self._playbook_basedir = dir # get group vars from group_vars/ files for group in self.groups: # FIXME: combine_vars group.vars = combine_vars(group.vars, self.get_group_vars(group, new_pb_basedir=True)) # get host vars from host_vars/ files for host in self.get_hosts(): # FIXME: combine_vars host.vars = combine_vars(host.vars, self.get_host_vars(host, new_pb_basedir=True)) # invalidate cache self._vars_per_host = {} self._vars_per_group = {}
def test_improper_args(self): with mock.patch("ansible.constants.DEFAULT_HASH_BEHAVIOUR", "replace"): with self.assertRaises(AnsibleError): combine_vars([1, 2, 3], dict(a=1)) with self.assertRaises(AnsibleError): combine_vars(dict(a=1), [1, 2, 3]) with mock.patch("ansible.constants.DEFAULT_HASH_BEHAVIOUR", "merge"): with self.assertRaises(AnsibleError): combine_vars([1, 2, 3], dict(a=1)) with self.assertRaises(AnsibleError): combine_vars(dict(a=1), [1, 2, 3])
def set_host_variable(self, host, varname, value): ''' Sets a value in the vars_cache for a host. ''' host_name = host.get_name() if host_name not in self._vars_cache: self._vars_cache[host_name] = dict() if varname in self._vars_cache[host_name]: self._vars_cache[host_name][varname] = combine_vars(self._vars_cache[host_name][varname], value) else: self._vars_cache[host_name][varname] = value
def set_host_variable(self, host, varname, value): ''' Sets a value in the vars_cache for a host. ''' host_name = host.get_name() if host_name not in self._vars_cache: self._vars_cache[host_name] = dict() if varname in self._vars_cache[host_name] and isinstance( self._vars_cache[host_name][varname], MutableMapping) and isinstance(value, MutableMapping): self._vars_cache[host_name] = combine_vars( self._vars_cache[host_name], {varname: value}) else: self._vars_cache[host_name][varname] = value
def _get_host_variables(self, host): if context.CLIARGS['export']: hostvars = host.get_vars() # FIXME: add switch to skip vars plugins # add vars plugin info for inventory_dir in self.inventory._sources: hostvars = combine_vars( hostvars, self.get_plugin_vars(inventory_dir, host)) else: hostvars = self.vm.get_vars(host=host, include_hostvars=False) return hostvars
def _get_host_variables(self, hostname, vault_password=None): host = self.get_host(hostname) if host is None: raise AnsibleError("host not found: %s" % hostname) vars = {} # plugin.run retrieves all vars (also from groups) for host vars_results = [ plugin.run(host, vault_password=vault_password) for plugin in self._vars_plugins if hasattr(plugin, 'run') ] for updated in vars_results: if updated is not None: vars = combine_vars(vars, updated) # plugin.get_host_vars retrieves just vars for specific host vars_results = [ plugin.get_host_vars(host, vault_password=vault_password) for plugin in self._vars_plugins if hasattr(plugin, 'get_host_vars') ] for updated in vars_results: if updated is not None: vars = combine_vars(vars, updated) # still need to check InventoryParser per host vars # which actually means InventoryScript per host, # which is not performant if self.parser is not None: vars = combine_vars(vars, self.parser.get_host_variables(host)) # Read host_vars/ files vars = combine_vars(vars, self.get_host_vars(host)) return vars
def parse_sources(self, cache=False): ''' iterate over inventory sources and parse each one to populate it''' parsed = False # allow for multiple inventory parsing for source in self._sources: if source: if ',' not in source: source = unfrackpath(source, follow=False) parse = self.parse_source(source, cache=cache) if parse and not parsed: parsed = True if parsed: # do post processing self._inventory.reconcile_inventory() else: if C.INVENTORY_UNPARSED_IS_FAILED: raise AnsibleError( "No inventory was parsed, please check your configuration and options." ) else: display.warning( "No inventory was parsed, only implicit localhost is available" ) for group in self.groups.values(): group.vars = combine_vars( group.vars, get_vars_from_inventory_sources(self._loader, self._sources, [group], 'inventory')) for host in self.hosts.values(): host.vars = combine_vars( host.vars, get_vars_from_inventory_sources(self._loader, self._sources, [host], 'inventory'))
def parse(self, inventory, loader, path, cache=False): ''' parses the inventory file ''' super(InventoryModule, self).parse(inventory, loader, path, cache=True) try: data = self.loader.load_from_file(path) except Exception as e: raise AnsibleParserError("Unable to parse %s: %s" % (to_native(path), to_native(e))) if not data: raise AnsibleParserError("%s is empty" % (to_native(path))) elif data.get('plugin') != self.NAME: raise AnsibleParserError( "%s is not a constructed groups config file, plugin entry must be 'constructed'" % (to_native(path))) strict = data.get('strict', False) try: # Go over hosts (less var copies) for host in inventory.hosts: # get available variables to templar hostvars = inventory.hosts[host].get_vars() if host in inventory.cache: # adds facts if cache is active hostvars = combine_vars(hostvars, inventory.cache[host]) # create composite vars self._set_composite_vars(data.get('compose'), hostvars, host, strict=strict) # constructed groups based on conditionals self._add_host_to_composed_groups(data.get('groups'), hostvars, host, strict=strict) # constructed groups based variable values self._add_host_to_keyed_groups(data.get('keyed_groups'), hostvars, host, strict=strict) except Exception as e: raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))
def parse(self, inventory, loader, path, cache=True): super(InventoryModule, self).parse(inventory, loader, path) self._read_config_data(path) self._connect_to_libvirt() self.inventory.add_group('libvirt') strict = self.get_option('strict') for dom in self._lv.listAllDomains(): host = dom.name() LOG.info('inspecting %s', host) if not self.get_option('include_inactive') and \ dom.state() != libvirt.VIR_DOMAIN_RUNNING: LOG.info('skipping %s (not running)', host) continue self.inventory.add_host(host) self.inventory.add_child('libvirt', host) address = self._lookup_dom_address(host, dom) if address is None: LOG.warning('failed to find address for %s', host) else: self.inventory.set_variable(host, 'ansible_host', address) self._set_libvirt_vars(host, dom) hostvars = combine_vars( get_group_vars(inventory.hosts[host].get_groups()), inventory.hosts[host].get_vars()) # create composite vars self._set_composite_vars(self.get_option('compose'), hostvars, host, strict=strict) # constructed groups based on conditionals self._add_host_to_composed_groups(self.get_option('groups'), hostvars, host, strict=strict) # constructed groups based variable values self._add_host_to_keyed_groups(self.get_option('keyed_groups'), hostvars, host, strict=strict)
def _get_host_variables(self, host): if context.CLIARGS['export']: # only get vars defined directly host hostvars = host.get_vars() # Always load vars plugins hostvars = combine_vars( hostvars, get_vars_from_inventory_sources(self.loader, self.inventory._sources, [host], 'all')) if context.CLIARGS['basedir']: hostvars = combine_vars( hostvars, get_vars_from_path(self.loader, context.CLIARGS['basedir'], [host], 'all')) else: # get all vars flattened by host, but skip magic hostvars hostvars = self.vm.get_vars(host=host, include_hostvars=False, stage='all') return self._remove_internal(hostvars)
def _get_group_variables(self, groupname, vault_password=None): group = self.get_group(groupname) if group is None: raise Exception("group not found: %s" % groupname) vars = {} # plugin.get_group_vars retrieves just vars for specific group vars_results = [ plugin.get_group_vars(group, vault_password=vault_password) for plugin in self._vars_plugins if hasattr(plugin, 'get_group_vars') ] for updated in vars_results: if updated is not None: # FIXME: combine_vars vars = combine_vars(vars, updated) # Read group_vars/ files # FIXME: combine_vars vars = combine_vars(vars, self.get_group_vars(group)) return vars
def _compose(self, template, variables): ''' helper method for plugins to compose variables for Ansible based on jinja2 expression and inventory vars''' t = self.templar try: use_extra = self.get_option('use_extra_vars') except Exception: use_extra = False if use_extra: t.available_variables = combine_vars(variables, self._vars) else: t.available_variables = variables return t.template('%s%s%s' % (t.environment.variable_start_string, template, t.environment.variable_end_string), disable_lookups=True)
def set_playbook_basedir(self, dir_name): """ sets the base directory of the playbook so inventory can use it as a basedir for host_ and group_vars, and other things. """ # Only update things if dir is a different playbook basedir if dir_name != self._playbook_basedir: self._playbook_basedir = dir_name # get group vars from group_vars/ files # FIXME: excluding the new_pb_basedir directory may result in group_vars # files loading more than they should, however with the file caching # we do this shouldn't be too much of an issue. Still, this should # be fixed at some point to allow a "first load" to touch all of the # directories, then later runs only touch the new basedir specified for group in self.groups: #group.vars = combine_vars(group.vars, self.get_group_vars(group, new_pb_basedir=True)) group.vars = combine_vars(group.vars, self.get_group_vars(group)) # get host vars from host_vars/ files for host in self.get_hosts(): #host.vars = combine_vars(host.vars, self.get_host_vars(host, new_pb_basedir=True)) host.vars = combine_vars(host.vars, self.get_host_vars(host)) # invalidate cache self._vars_per_host = {} self._vars_per_group = {}
def _get_group_variables(self, group): # get info from inventory source res = group.get_vars() res = combine_vars( res, get_vars_from_inventory_sources(self.loader, self.inventory._sources, [group], 'inventory')) if group.priority != 1: res['ansible_group_priority'] = group.priority return self._remove_internal(res)
def _get_host_variables(self, host): if context.CLIARGS['export']: # only get vars defined directly host hostvars = host.get_vars() # FIXME: add switch to skip vars plugins, add vars plugin info for inventory_dir in self.inventory._sources: hostvars = combine_vars( hostvars, self.get_plugin_vars(inventory_dir, host)) else: # get all vars flattened by host, but skip magic hostvars hostvars = self.vm.get_vars(host=host, include_hostvars=False) return self._remove_internal(hostvars)
def _load_role_yaml(self, subdir, main=None, allow_dir=False): ''' Find and load role YAML files and return data found. :param subdir: subdir of role to search (vars, files, tasks, handlers, defaults) :type subdir: string :param main: filename to match, will default to 'main.<ext>' if not provided. :type main: string :param allow_dir: If true we combine results of multiple matching files found. If false, highlander rules. Only for vars(dicts) and not tasks(lists). :type allow_dir: bool :returns: data from the matched file(s), type can be dict or list depending on vars or tasks. ''' data = None file_path = os.path.join(self._role_path, subdir) if self._loader.path_exists(file_path) and self._loader.is_directory(file_path): # Valid extensions and ordering for roles is hard-coded to maintain portability extensions = ['.yml', '.yaml', '.json'] # same as default for YAML_FILENAME_EXTENSIONS # look for files w/o extensions before/after bare name depending on it being set or not # keep 'main' as original to figure out errors if no files found if main is None: _main = 'main' extensions.append('') else: _main = main extensions.insert(0, '') # not really 'find_vars_files' but find_files_with_extensions_default_to_yaml_filename_extensions found_files = self._loader.find_vars_files(file_path, _main, extensions, allow_dir) if found_files: for found in found_files: new_data = self._loader.load_from_file(found) if new_data: if data is not None and isinstance(new_data, Mapping): data = combine_vars(data, new_data) else: data = new_data # found data so no need to continue unless we want to merge if not allow_dir: break elif main is not None: # this won't trigger with default only when <subdir>_from is specified raise AnsibleParserError("Could not find specified file in role: %s/%s" % (subdir, main)) return data
def _load_inventory_file(self, path, loader): ''' helper function, which loads the file and gets the basename of the file without the extension ''' if loader.is_directory(path): data = dict() try: names = loader.list_directory(path) except os.error as err: raise AnsibleError("This folder cannot be listed: %s: %s." % (path, err.strerror)) # evaluate files in a stable order rather than whatever # order the filesystem lists them. names.sort() # do not parse hidden files or dirs, e.g. .svn/ paths = [os.path.join(path, name) for name in names if not name.startswith('.')] for p in paths: results = self._load_inventory_file(path=p, loader=loader) if results is not None: data = combine_vars(data, results) else: file_name, ext = os.path.splitext(path) data = None if not ext or ext not in C.YAML_FILENAME_EXTENSIONS: for test_ext in C.YAML_FILENAME_EXTENSIONS: new_path = path + test_ext if loader.path_exists(new_path): data = loader.load_from_file(new_path) break else: if loader.path_exists(path): data = loader.load_from_file(path) rval = AnsibleInventoryVarsData() rval.path = path if data is not None: if not isinstance(data, dict): raise AnsibleError("Problem parsing file '%s': line %d, column %d" % data.ansible_pos) else: rval.update(data) return rval
def get_vars(self, loader, path, entities): super(VarsModule, self).get_vars(loader, path, entities) out_vars = {} cache = self.become_pass_cache for entity in entities: if entity not in cache: cache[entity] = self.get_password(entity) password = cache[entity] if password is not None: out_vars = combine_vars(out_vars, {'ansible_become_pass': password}) return out_vars
def _plugins_inventory(entities): ''' merges all entities by inventory source ''' data = {} for inventory_dir in self._inventory._sources: if ',' in inventory_dir: # skip host lists continue elif not os.path.isdir( inventory_dir ): # always pass 'inventory directory' inventory_dir = os.path.dirname(inventory_dir) for plugin in vars_loader.all(): data = combine_vars( data, _get_plugin_vars(plugin, inventory_dir, entities)) return data
def _get_host_variables(self, host): if self.options.export: hostvars = host.get_vars() # FIXME: add switch to skip vars plugins # add vars plugin info for inventory_dir in self.inventory._sources: hostvars = combine_vars(hostvars, self.get_plugin_vars(inventory_dir, host)) else: if self._new_api: hostvars = self.vm.get_vars(host=host, include_hostvars=False) else: hostvars = self.vm.get_vars(self.loader, host=host, include_hostvars=False) return hostvars
def _add_host_to_keyed_groups(self, keys, variables, host, strict=False): ''' helper to create groups for plugins based on variable values and add the corresponding hosts to it''' if keys and isinstance(keys, list): for keyed in keys: if keyed and isinstance(keyed, dict): variables = combine_vars(variables, self.inventory.get_host(host).get_vars()) try: key = self._compose(keyed.get('key'), variables) except Exception as e: if strict: raise AnsibleParserError("Could not generate group for host %s from %s entry: %s" % (host, keyed.get('key'), to_native(e))) continue if key: prefix = keyed.get('prefix', '') sep = keyed.get('separator', '_') raw_parent_name = keyed.get('parent_group', None) new_raw_group_names = [] if isinstance(key, string_types): new_raw_group_names.append(key) elif isinstance(key, list): for name in key: new_raw_group_names.append(name) elif isinstance(key, Mapping): for (gname, gval) in key.items(): name = '%s%s%s' % (gname, sep, gval) new_raw_group_names.append(name) else: raise AnsibleParserError("Invalid group name format, expected a string or a list of them or dictionary, got: %s" % type(key)) for bare_name in new_raw_group_names: gname = to_safe_group_name('%s%s%s' % (prefix, sep, bare_name)) self.inventory.add_group(gname) self.inventory.add_child(gname, host) if raw_parent_name: parent_name = to_safe_group_name(raw_parent_name) self.inventory.add_group(parent_name) self.inventory.add_child(parent_name, gname) else: if strict: raise AnsibleParserError("No key or key resulted empty, invalid entry") else: raise AnsibleParserError("Invalid keyed group entry, it must be a dictionary: %s " % keyed)
def set_host_overrides(self, host): ''' Override WinRM-specific options from host variables. ''' host_vars = combine_vars(host.get_group_vars(), host.get_vars()) self._winrm_host = self._play_context.remote_addr self._winrm_port = int(self._play_context.port or 5986) self._winrm_scheme = host_vars.get( 'ansible_winrm_scheme', 'http' if self._winrm_port == 5985 else 'https') self._winrm_path = host_vars.get('ansible_winrm_path', '/wsman') self._winrm_user = self._play_context.remote_user self._winrm_pass = self._play_context.password if '@' in self._winrm_user: self._winrm_realm = self._winrm_user.split('@', 1)[1].strip() or None else: self._winrm_realm = None self._winrm_realm = host_vars.get('ansible_winrm_realm', self._winrm_realm) or None transport_selector = 'ssl' if self._winrm_scheme == 'https' else 'plaintext' if HAVE_KERBEROS and ('@' in self._winrm_user or self._winrm_realm): self._winrm_transport = 'kerberos,%s' % transport_selector else: self._winrm_transport = transport_selector self._winrm_transport = host_vars.get('ansible_winrm_transport', self._winrm_transport) if isinstance(self._winrm_transport, basestring): self._winrm_transport = [ x.strip() for x in self._winrm_transport.split(',') if x.strip() ] self._winrm_kwargs = dict(username=self._winrm_user, password=self._winrm_pass, realm=self._winrm_realm) argspec = inspect.getargspec(Protocol.__init__) for arg in argspec.args: if arg in ('self', 'endpoint', 'transport', 'username', 'password', 'realm'): continue if 'ansible_winrm_%s' % arg in host_vars: self._winrm_kwargs[arg] = host_vars['ansible_winrm_%s' % arg]
def reconcile_inventory(self): ''' Ensure inventory basic rules, run after updates ''' display.debug('Reconcile groups and hosts in inventory.') self.current_source = None group_names = set() # set group vars from group_vars/ files and vars plugins for g in self.groups: group = self.groups[g] group_names.add(group.name) # ensure all groups inherit from 'all' if group.name != 'all' and not group.get_ancestors(): self.add_child('all', group.name) host_names = set() # get host vars from host_vars/ files and vars plugins for host in self.hosts.values(): host_names.add(host.name) mygroups = host.get_groups() if self.groups['ungrouped'] in mygroups: # clear ungrouped of any incorrectly stored by parser if set(mygroups).difference( set([self.groups['all'], self.groups['ungrouped']])): self.groups['ungrouped'].remove_host(host) elif not host.implicit: # add ungrouped hosts to ungrouped, except implicit length = len(mygroups) if length == 0 or (length == 1 and self.groups['all'] in mygroups): self.add_child('ungrouped', host.name) # special case for implicit hosts if host.implicit: host.vars = combine_vars(self.groups['all'].get_vars(), host.vars) # warn if overloading identifier as both group and host for conflict in group_names.intersection(host_names): display.warning("Found both group and host with same name: %s" % conflict) self._groups_dict_cache = {}
def get_vars_from_inventory_sources(loader, sources, entities, stage): data = {} for path in sources: if path is None: continue if ',' in path and not os.path.exists(path): # skip host lists continue elif not os.path.isdir(to_bytes(path)): # always pass the directory of the inventory source file path = os.path.dirname(path) data = combine_vars(data, get_vars_from_path(loader, path, entities, stage)) return data
def get_vars(self, loader, path, entities, cache=True): ''' parses the inventory file ''' if not isinstance(entities, list): entities = [entities] super(VarsModule, self).get_vars(loader, path, entities) data = {} for entity in entities: if isinstance(entity, Host): subdir = 'host_vars' elif isinstance(entity, Group): subdir = 'group_vars' else: raise AnsibleParserError("Supplied entity must be Host or Group, got %s instead" % (type(entity))) # avoid 'chroot' type inventory hostnames /path/to/chroot if not entity.name.startswith(os.path.sep): try: found_files = [] # load vars opath = os.path.realpath(os.path.join(self._basedir, subdir)) key = '%s.%s' % (entity.name, opath) if cache and key in FOUND: found_files = FOUND[key] else: b_opath = to_bytes(opath) # no need to do much if path does not exist for basedir if os.path.exists(b_opath): if os.path.isdir(b_opath): self._display.debug("\tprocessing dir %s" % opath) found_files = self._find_vars_files(opath, entity.name) FOUND[key] = found_files else: self._display.warning("Found %s that is not a directory, skipping: %s" % (subdir, opath)) for found in found_files: new_data = loader.load_from_file(found, cache=True, unsafe=True) if new_data: # ignore empty files data = combine_vars(data, new_data) except Exception as e: raise AnsibleParserError(to_native(e)) return data
def _plugins_inventory(self, entities): import os from ansible.plugins.loader import vars_loader from ansible.utils.vars import combine_vars ''' merges all entities by inventory source ''' data = {} for inventory_dir in self.variable_manager._inventory._sources: if ',' in inventory_dir: # skip host lists continue elif not os.path.isdir( inventory_dir): # always pass 'inventory directory' inventory_dir = os.path.dirname(inventory_dir) for plugin in vars_loader.all(): data = combine_vars( data, self._get_plugin_vars(plugin, inventory_dir, entities)) return data
def get_vars(self, loader, path, entities): ''' parses the inventory file ''' if not isinstance(entities, list): entities = [entities] super(VarsModule, self).get_vars(loader, path, entities) data = {} for entity in entities: if isinstance(entity, Host): subdir = 'host_vars' elif isinstance(entity, Group): subdir = 'group_vars' else: raise AnsibleParserError( "Supplied entity must be Host or Group, got %s instead" % (type(entity))) try: # load vars opath = os.path.realpath(os.path.join(self._basedir, subdir)) b_opath = to_bytes(opath) # no need to do much if path does not exist for basedir if os.path.exists(b_opath): if os.path.isdir(b_opath): self._display.debug("\tprocessing dir %s" % opath) for found in self._find_vars_files(opath, entity.name): self._display.debug("READING %s" % found) new_data = loader.load_from_file(found, cache=True, unsafe=True) if new_data: # ignore empty files data = combine_vars(data, new_data) else: self._display.warning( "Found %s that is not a directory, skipping: %s" % (subdir, opath)) except Exception as e: raise AnsibleParserError(to_text(e)) return data
def _add_host_to_composed_groups(self, groups, variables, host, strict=False): ''' helper to create complex groups for plugins based on jinja2 conditionals, hosts that meet the conditional are added to group''' # process each 'group entry' if groups and isinstance(groups, dict): variables = combine_vars(variables, self.inventory.get_host(host).get_vars()) self.templar.set_available_variables(variables) for group_name in groups: conditional = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % groups[group_name] try: result = boolean(self.templar.template(conditional)) except Exception as e: if strict: raise AnsibleParserError("Could not add host %s to group %s: %s" % (host, group_name, to_native(e))) continue if result: # ensure group exists self.inventory.add_group(group_name) # add host to group self.inventory.add_child(group_name, host)
def _get_persistent_connection_options(self, connection, variables, templar): final_vars = combine_vars( variables, variables.get('ansible_delegated_vars', dict()).get(self._task.delegate_to, dict())) option_vars = C.config.get_plugin_vars('connection', connection._load_name) for plugin in connection._sub_plugins: if plugin['type'] != 'external': option_vars.extend( C.config.get_plugin_vars(plugin['type'], plugin['name'])) options = {} for k in option_vars: if k in final_vars: options[k] = templar.template(final_vars[k]) return options
def parse(self, inventory, loader, path): ''' parses the inventory file ''' super(InventoryModule, self).parse(inventory, loader, path) try: data = self.loader.load_from_file(path) except Exception as e: raise AnsibleParserError("Unable to parse %s: %s" % (to_native(path), to_native(e))) if not data or data.get('plugin') != self.NAME: raise AnsibleParserError( "%s is empty or not a constructed groups config file" % (to_native(path))) try: templar = Templar(loader=loader) # Go over hosts (less var copies) for host in inventory.hosts: # get available variables to templar hostvars = host.get_vars() if host.name in inventory.cache: # adds facts if cache is active hostvars = combine_vars(hostvars, inventory.cache[host.name]) templar.set_available_variables(hostvars) # process each 'group entry' for group_name, expression in data.get('groups', {}): conditional = u"{%% if %s %%} True {%% else %%} False {%% endif %%}" % expression result = templar.template(conditional) if result and bool(result): # ensure group exists inventory.add_group(group_name) # add host to group inventory.add_child(group_name, host.name) except Exception as e: raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))
def _merge_hosts(self,host, newhost): """ Merge all of instance newhost into host """ # name if host.name != newhost.name: raise AnsibleError("Cannot merge host %s with %s" % (host.name, newhost.name)) # group membership relation for newgroup in newhost.groups: # dict with existing groups: hostgroups = dict([(g.name, g) for g in host.groups]) # check if new group is already known as a group if newgroup.name not in hostgroups: if newgroup.name not in self.groups: # group does not exist yet in self, import him self.groups[newgroup.name] = newgroup # group now exists but doesn't have host yet self.groups[newgroup.name].add_host(host) # variables host.vars = combine_vars(host.vars, newhost.vars)
def get_vars(self, loader, path, entities): """Entry point called from Ansible to get vars.""" if self.vault_addr is None: debug("VAULT_ADDR isnt set, skipping hashivault_vars plugin") return {} debug("get_vars **********************************") if not isinstance(entities, list): entities = [entities] debug("lookup entities:", entities) super(VarsModule, self).get_vars(loader, path, entities) data = {} for entity in entities: data = combine_vars(data, entity.vars) data = self._get_vars(data, entity) debug("get_vars: ", data) return data
def get_vars_from_path(loader, path, entities, stage): data = {} vars_plugin_list = list(vars_loader.all()) for plugin_name in C.VARIABLE_PLUGINS_ENABLED: if AnsibleCollectionRef.is_valid_fqcr(plugin_name): vars_plugin = vars_loader.get(plugin_name) if vars_plugin is None: # Error if there's no play directory or the name is wrong? continue if vars_plugin not in vars_plugin_list: vars_plugin_list.append(vars_plugin) for plugin in vars_plugin_list: if plugin._load_name not in C.VARIABLE_PLUGINS_ENABLED and getattr( plugin, 'REQUIRES_WHITELIST', False): # 2.x plugins shipped with ansible should require enabling, older or non shipped should load automatically continue has_stage = hasattr(plugin, 'get_option') and plugin.has_option('stage') # if a plugin-specific setting has not been provided, use the global setting # older/non shipped plugins that don't support the plugin-specific setting should also use the global setting use_global = (has_stage and plugin.get_option('stage') is None) or not has_stage if use_global: if C.RUN_VARS_PLUGINS == 'demand' and stage == 'inventory': continue elif C.RUN_VARS_PLUGINS == 'start' and stage == 'task': continue elif has_stage and plugin.get_option('stage') not in ('all', stage): continue data = combine_vars(data, get_plugin_vars(loader, plugin, path, entities)) return data