def _load_file(self, file_name): if not file_name or not isinstance(file_name, string_types): raise AssibleParserError("Invalid filename: '%s'" % to_native(file_name)) b_file_name = to_bytes(self.loader.path_dwim(file_name)) if not self.loader.path_exists(b_file_name): raise AssibleFileNotFound("Unable to retrieve file contents", file_name=file_name) try: (b_data, private) = self.loader._get_file_contents(file_name) return toml.loads(to_text(b_data, errors='surrogate_or_strict')) except toml.TomlDecodeError as e: raise AssibleParserError( 'TOML file (%s) is invalid: %s' % (file_name, to_native(e)), orig_exc=e ) except (IOError, OSError) as e: raise AssibleParserError( "An error occurred while trying to read the file '%s': %s" % (file_name, to_native(e)), orig_exc=e ) except Exception as e: raise AssibleParserError( "An unexpected error occurred while parsing the file '%s': %s" % (file_name, to_native(e)), orig_exc=e )
def parse(self, inventory, loader, path, cache=True): ''' parses the inventory file ''' super(InventoryModule, self).parse(inventory, loader, path) self.set_options() try: data = self.loader.load_from_file(path, cache=False) except Exception as e: raise AssibleParserError(e) if not data: raise AssibleParserError('Parsed empty YAML file') elif not isinstance(data, MutableMapping): raise AssibleParserError( 'YAML inventory has invalid structure, it should be a dictionary, got: %s' % type(data)) elif data.get('plugin'): raise AssibleParserError( 'Plugin configuration YAML file, not YAML inventory') # We expect top level keys to correspond to groups, iterate over them # to get host, vars and subgroups (which we iterate over recursivelly) if isinstance(data, MutableMapping): for group_name in data: self._parse_group(group_name, data[group_name]) else: raise AssibleParserError( "Invalid data from file, expected dictionary and got:\n\n%s" % to_native(data))
def preprocess_data(self, ds): ''' Regorganizes the data for a PlaybookInclude datastructure to line up with what we expect the proper attributes to be ''' if not isinstance(ds, dict): raise AssibleAssertionError( 'ds (%s) should be a dict but was a %s' % (ds, type(ds))) # the new, cleaned datastructure, which will have legacy # items reduced to a standard structure new_ds = AssibleMapping() if isinstance(ds, AssibleBaseYAMLObject): new_ds.assible_pos = ds.assible_pos for (k, v) in iteritems(ds): if k in ('include', 'import_playbook'): self._preprocess_import(ds, new_ds, k, v) else: # some basic error checking, to make sure vars are properly # formatted and do not conflict with k=v parameters if k == 'vars': if 'vars' in new_ds: raise AssibleParserError( "import_playbook parameters cannot be mixed with 'vars' entries for import statements", obj=ds) elif not isinstance(v, dict): raise AssibleParserError( "vars for import_playbook statements must be specified as a dictionary", obj=ds) new_ds[k] = v return super(PlaybookInclude, self).preprocess_data(new_ds)
def check_options(self, task, data): ''' Method for options validation to use in 'load_data' for TaskInclude and HandlerTaskInclude since they share the same validations. It is not named 'validate_options' on purpose to prevent confusion with '_validate_*" methods. Note that the task passed might be changed as a side-effect of this method. ''' my_arg_names = frozenset(task.args.keys()) # validate bad args, otherwise we silently ignore bad_opts = my_arg_names.difference(self.VALID_ARGS) if bad_opts and task.action in ('include_tasks', 'import_tasks'): raise AssibleParserError('Invalid options for %s: %s' % (task.action, ','.join(list(bad_opts))), obj=data) if not task.args.get('_raw_params'): task.args['_raw_params'] = task.args.pop('file', None) if not task.args['_raw_params']: raise AssibleParserError('No file specified for %s' % task.action) apply_attrs = task.args.get('apply', {}) if apply_attrs and task.action != 'include_tasks': raise AssibleParserError('Invalid options for %s: apply' % task.action, obj=data) elif not isinstance(apply_attrs, dict): raise AssibleParserError( 'Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data) return task
def _get_file_contents(self, file_name): ''' Reads the file contents from the given file name If the contents are vault-encrypted, it will decrypt them and return the decrypted data :arg file_name: The name of the file to read. If this is a relative path, it will be expanded relative to the basedir :raises AssibleFileNotFound: if the file_name does not refer to a file :raises AssibleParserError: if we were unable to read the file :return: Returns a byte string of the file contents ''' if not file_name or not isinstance(file_name, (binary_type, text_type)): raise AssibleParserError("Invalid filename: '%s'" % to_native(file_name)) b_file_name = to_bytes(self.path_dwim(file_name)) # This is what we really want but have to fix unittests to make it pass # if not os.path.exists(b_file_name) or not os.path.isfile(b_file_name): if not self.path_exists(b_file_name): raise AssibleFileNotFound("Unable to retrieve file contents", file_name=file_name) try: with open(b_file_name, 'rb') as f: data = f.read() return self._decrypt_if_vault_data(data, b_file_name) except (IOError, OSError) as e: raise AssibleParserError( "an error occurred while trying to read the file '%s': %s" % (file_name, to_native(e)), orig_exc=e)
def parse_kv(args, check_raw=False): ''' Convert a string of key/value items to a dict. If any free-form params are found and the check_raw option is set to True, they will be added to a new parameter called '_raw_params'. If check_raw is not enabled, they will simply be ignored. ''' args = to_text(args, nonstring='passthru') options = {} if args is not None: try: vargs = split_args(args) except IndexError as e: raise AssibleParserError("Unable to parse argument string", orig_exc=e) except ValueError as ve: if 'no closing quotation' in str(ve).lower(): raise AssibleParserError( "error parsing argument string, try quoting the entire line.", orig_exc=ve) else: raise raw_params = [] for orig_x in vargs: x = _decode_escapes(orig_x) if "=" in x: pos = 0 try: while True: pos = x.index('=', pos + 1) if pos > 0 and x[pos - 1] != '\\': break except ValueError: # ran out of string, but we must have some escaped equals, # so replace those and append this to the list of raw params raw_params.append(x.replace('\\=', '=')) continue k = x[:pos] v = x[pos + 1:] # FIXME: make the retrieval of this list of shell/command # options a function, so the list is centralized if check_raw and k not in ('creates', 'removes', 'chdir', 'executable', 'warn'): raw_params.append(orig_x) else: options[k.strip()] = unquote(v.strip()) else: raw_params.append(orig_x) # recombine the free-form params, if any were found, and assign # them to a special option for use later by the shell/command module if len(raw_params) > 0: options[u'_raw_params'] = join_args(raw_params) return options
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 AssibleParserError("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 AssibleParserError("Invalid variable name in vars specified for %s: %s" % (self.__class__.__name__, e), obj=ds, orig_exc=e)
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 AssibleParserError( "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 b_opath = os.path.realpath( to_bytes(os.path.join(self._basedir, subdir))) opath = to_text(b_opath) key = '%s.%s' % (entity.name, opath) if cache and key in FOUND: found_files = FOUND[key] else: # 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 = loader.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 AssibleParserError(to_native(e)) return data
def _normalize_parameters(self, thing, action=None, additional_args=None): ''' arguments can be fuzzy. Deal with all the forms. ''' additional_args = {} if additional_args is None else additional_args # final args are the ones we'll eventually return, so first update # them with any additional args specified, which have lower priority # than those which may be parsed/normalized next final_args = dict() if additional_args: if isinstance(additional_args, string_types): templar = Templar(loader=None) if templar.is_template(additional_args): final_args['_variable_params'] = additional_args else: raise AssibleParserError("Complex args containing variables cannot use bare variables (without Jinja2 delimiters), " "and must use the full variable style ('{{var_name}}')") elif isinstance(additional_args, dict): final_args.update(additional_args) else: raise AssibleParserError('Complex args must be a dictionary or variable string ("{{var}}").') # how we normalize depends if we figured out what the module name is # yet. If we have already figured it out, it's a 'new style' invocation. # otherwise, it's not if action is not None: args = self._normalize_new_style_args(thing, action) else: (action, args) = self._normalize_old_style_args(thing) # this can occasionally happen, simplify if args and 'args' in args: tmp_args = args.pop('args') if isinstance(tmp_args, string_types): tmp_args = parse_kv(tmp_args) args.update(tmp_args) # only internal variables can start with an underscore, so # we don't allow users to set them directly in arguments if args and action not in FREEFORM_ACTIONS: for arg in args: arg = to_text(arg) if arg.startswith('_assible_'): raise AssibleError("invalid parameter specified for action '%s': '%s'" % (action, arg)) # finally, update the args we're going to return with the ones # which were normalized above if args: final_args.update(args) return (action, final_args)
def get_validated_value(self, name, attribute, value, templar): if attribute.isa == 'string': value = to_text(value) elif attribute.isa == 'int': value = int(value) elif attribute.isa == 'float': value = float(value) elif attribute.isa == 'bool': value = boolean(value, strict=True) elif attribute.isa == 'percent': # special value, which may be an integer or float # with an optional '%' at the end if isinstance(value, string_types) and '%' in value: value = value.replace('%', '') value = float(value) elif attribute.isa == 'list': if value is None: value = [] elif not isinstance(value, list): value = [value] if attribute.listof is not None: for item in value: if not isinstance(item, attribute.listof): raise AssibleParserError("the field '%s' should be a list of %s, " "but the item '%s' is a %s" % (name, attribute.listof, item, type(item)), obj=self.get_ds()) elif attribute.required and attribute.listof == string_types: if item is None or item.strip() == "": raise AssibleParserError("the field '%s' is required, and cannot have empty values" % (name,), obj=self.get_ds()) elif attribute.isa == 'set': if value is None: value = set() elif not isinstance(value, (list, set)): if isinstance(value, string_types): value = value.split(',') else: # Making a list like this handles strings of # text and bytes properly value = [value] if not isinstance(value, set): value = set(value) elif attribute.isa == 'dict': if value is None: value = dict() elif not isinstance(value, dict): raise TypeError("%s is not a dictionary" % value) elif attribute.isa == 'class': if not isinstance(value, attribute.class_type): raise TypeError("%s is not a valid %s (got a %s instead)" % (name, attribute.class_type, type(value))) value.post_validate(templar=templar) return value
def preprocess_data(self, ds): ''' Adjusts play datastructure to cleanup old/legacy items ''' if not isinstance(ds, dict): raise AssibleAssertionError( 'while preprocessing data (%s), ds should be a dict but was a %s' % (ds, type(ds))) # The use of 'user' in the Play datastructure was deprecated to # line up with the same change for Tasks, due to the fact that # 'user' conflicted with the user module. if 'user' in ds: # this should never happen, but error out with a helpful message # to the user if it does... if 'remote_user' in ds: raise AssibleParserError( "both 'user' and 'remote_user' are set for %s. " "The use of 'user' is deprecated, and should be removed" % self.get_name(), obj=ds) ds['remote_user'] = ds['user'] del ds['user'] return super(Play, self).preprocess_data(ds)
def validate(self, all_vars=None): ''' validation that is done at parse time, not load time ''' all_vars = {} if all_vars is None else all_vars if not self._validated: # walk all fields in the object for (name, attribute) in iteritems(self._valid_attrs): if name in self._alias_attrs: name = self._alias_attrs[name] # run validator only if present method = getattr(self, '_validate_%s' % name, None) if method: method(attribute, name, getattr(self, name)) else: # and make sure the attribute is of the type it should be value = self._attributes[name] if value is not None: if attribute.isa == 'string' and isinstance(value, (list, dict)): raise AssibleParserError( "The field '%s' is supposed to be a string type," " however the incoming data structure is a %s" % (name, type(value)), obj=self.get_ds() ) self._validated = True
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.available_variables = variables for group_name in groups: conditional = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % groups[ group_name] group_name = original_safe(group_name, force=True) try: result = boolean(self.templar.template(conditional)) except Exception as e: if strict: raise AssibleParserError( "Could not add host %s to group %s: %s" % (host, group_name, to_native(e))) continue if result: # ensure group exists, use sanitized name group_name = self.inventory.add_group(group_name) # add host to group self.inventory.add_child(group_name, host)
def _load_roles(self, attr, ds): ''' Loads and returns a list of RoleInclude objects from the datastructure list of role definitions and creates the Role from those objects ''' if ds is None: ds = [] try: role_includes = load_list_of_roles( ds, play=self, variable_manager=self._variable_manager, loader=self._loader, collection_search_list=self.collections) except AssertionError as e: raise AssibleParserError( "A malformed role declaration was encountered.", obj=self._ds, orig_exc=e) roles = [] for ri in role_includes: roles.append(Role.load(ri, play=self)) self.roles[:0] = roles return self.roles
def run(self, terms, variables=None, **kwargs): ret = [] for term in terms: display.debug("File lookup term: %s" % term) # Find the file in the expected search path lookupfile = self.find_file_in_search_path(variables, 'files', term) display.vvvv(u"File lookup using %s as file" % lookupfile) try: if lookupfile: b_contents, show_data = self._loader._get_file_contents(lookupfile) contents = to_text(b_contents, errors='surrogate_or_strict') if kwargs.get('lstrip', False): contents = contents.lstrip() if kwargs.get('rstrip', True): contents = contents.rstrip() ret.append(contents) else: raise AssibleParserError() except AssibleParserError: raise AssibleError("could not locate file in lookup: %s" % term) return ret
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 AssibleParserError( "Could not find specified file in role: %s/%s" % (subdir, main)) return None
def from_yaml(data, file_name='<string>', show_content=True, vault_secrets=None, json_only=False): ''' Creates a python datastructure from the given data, which can be either a JSON or YAML string. ''' new_data = None try: # in case we have to deal with vaults AssibleJSONDecoder.set_secrets(vault_secrets) # we first try to load this data as JSON. # Fixes issues with extra vars json strings not being parsed correctly by the yaml parser new_data = json.loads(data, cls=AssibleJSONDecoder) except Exception as json_exc: if json_only: raise AssibleParserError(to_native(json_exc), orig_exc=json_exc) # must not be JSON, let the rest try try: new_data = _safe_load(data, file_name=file_name, vault_secrets=vault_secrets) except YAMLError as yaml_exc: _handle_error(json_exc, yaml_exc, file_name, show_content) return new_data
def _normalize_new_style_args(self, thing, action): ''' deals with fuzziness in new style module invocations accepting key=value pairs and dictionaries, and returns a dictionary of arguments possible example inputs: 'echo hi', 'shell' {'region': 'xyz'}, 'ec2' standardized outputs like: { _raw_params: 'echo hi', _uses_shell: True } ''' if isinstance(thing, dict): # form is like: { xyz: { x: 2, y: 3 } } args = thing elif isinstance(thing, string_types): # form is like: copy: src=a dest=b check_raw = action in FREEFORM_ACTIONS args = parse_kv(thing, check_raw=check_raw) elif thing is None: # this can happen with modules which take no params, like ping: args = None else: raise AssibleParserError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds) return args
def get_real_file(self, file_path, decrypt=True): """ If the file is vault encrypted return a path to a temporary decrypted file If the file is not encrypted then the path is returned Temporary files are cleanup in the destructor """ if not file_path or not isinstance(file_path, (binary_type, text_type)): raise AssibleParserError("Invalid filename: '%s'" % to_native(file_path)) b_file_path = to_bytes(file_path, errors='surrogate_or_strict') if not self.path_exists(b_file_path) or not self.is_file(b_file_path): raise AssibleFileNotFound(file_name=file_path) real_path = self.path_dwim(file_path) try: if decrypt: with open(to_bytes(real_path), 'rb') as f: # Limit how much of the file is read since we do not know # whether this is a vault file and therefore it could be very # large. if is_encrypted_file(f, count=len(b_HEADER)): # if the file is encrypted and no password was specified, # the decrypt call would throw an error, but we check first # since the decrypt function doesn't know the file name data = f.read() if not self._vault.secrets: raise AssibleParserError( "A vault password or secret must be specified to decrypt %s" % to_native(file_path)) data = self._vault.decrypt(data, filename=real_path) # Make a temp file real_path = self._create_content_tempfile(data) self._tempfiles.add(real_path) return real_path except (IOError, OSError) as e: raise AssibleParserError( "an error occurred while trying to read the file '%s': %s" % (to_native(real_path), to_native(e)), orig_exc=e)
def _load_loop_control(self, attr, ds): if not isinstance(ds, dict): raise AssibleParserError( "the `loop_control` value must be specified as a dictionary and cannot " "be a variable itself (though it can contain variables)", obj=ds, ) return LoopControl.load(data=ds, variable_manager=self._variable_manager, loader=self._loader)
def _validate_attributes(self, ds): ''' Ensures that there are no keys in the datastructure which do not map to attributes for this object. ''' valid_attrs = frozenset(self._valid_attrs.keys()) for key in ds: if key not in valid_attrs: raise AssibleParserError("'%s' is not a valid attribute for a %s" % (key, self.__class__.__name__), obj=ds)
def _parse_group(self, group, group_data): if group_data is not None and not isinstance(group_data, MutableMapping): self.display.warning("Skipping '%s' as this is not a valid group definition" % group) return group = self.inventory.add_group(group) if group_data is None: return for key, data in group_data.items(): if key == 'vars': if not isinstance(data, MutableMapping): raise AssibleParserError( 'Invalid "vars" entry for "%s" group, requires a dict, found "%s" instead.' % (group, type(data)) ) for var, value in data.items(): self.inventory.set_variable(group, var, value) elif key == 'children': if not isinstance(data, MutableSequence): raise AssibleParserError( 'Invalid "children" entry for "%s" group, requires a list, found "%s" instead.' % (group, type(data)) ) for subgroup in data: self._parse_group(subgroup, {}) self.inventory.add_child(group, subgroup) elif key == 'hosts': if not isinstance(data, MutableMapping): raise AssibleParserError( 'Invalid "hosts" entry for "%s" group, requires a dict, found "%s" instead.' % (group, type(data)) ) for host_pattern, value in data.items(): hosts, port = self._expand_hostpattern(host_pattern) self._populate_host_vars(hosts, value, group, port) else: self.display.warning( 'Skipping unexpected key "%s" in group "%s", only "vars", "children" and "hosts" are valid' % (key, group) )
def _populate_host_vars(self, hosts, variables, group=None, port=None): if not isinstance(variables, Mapping): raise AssibleParserError( "Invalid data from file, expected dictionary and got:\n\n%s" % to_native(variables)) for host in hosts: self.inventory.add_host(host, group=group, port=port) for k in variables: self.inventory.set_variable(host, k, variables[k])
def _load_vars_prompt(self, attr, ds): new_ds = preprocess_vars(ds) vars_prompts = [] if new_ds is not None: for prompt_data in new_ds: if 'name' not in prompt_data: raise AssibleParserError( "Invalid vars_prompt data structure, missing 'name' key", obj=ds) for key in prompt_data: if key not in ('name', 'prompt', 'default', 'private', 'confirm', 'encrypt', 'salt_size', 'salt', 'unsafe'): raise AssibleParserError( "Invalid vars_prompt data structure, found unsupported key '%s'" % key, obj=ds) vars_prompts.append(prompt_data) return vars_prompts
def _read_config_data(self, path): ''' validate config and set options as appropriate :arg path: path to common yaml format config file for this plugin ''' config = {} try: # avoid loader cache so meta: refresh_inventory can pick up config changes # if we read more than once, fs cache should be good enough config = self.loader.load_from_file(path, cache=False) except Exception as e: raise AssibleParserError(to_native(e)) # a plugin can be loaded via many different names with redirection- if so, we want to accept any of those names valid_names = getattr(self, '_redirected_names') or [self.NAME] if not config: # no data raise AssibleParserError("%s is empty" % (to_native(path))) elif config.get('plugin') not in valid_names: # this is not my config file raise AssibleParserError("Incorrect plugin name in file: %s" % config.get('plugin', 'none found')) elif not isinstance(config, Mapping): # configs are dictionaries raise AssibleParserError( 'inventory source has invalid structure, it should be a dictionary, got: %s' % type(config)) self.set_options(direct=config, var_options=self._vars) if 'cache' in self._options and self.get_option('cache'): cache_option_keys = [('_uri', 'cache_connection'), ('_timeout', 'cache_timeout'), ('_prefix', 'cache_prefix')] cache_options = dict((opt[0], self.get_option(opt[1])) for opt in cache_option_keys if self.get_option(opt[1]) is not None) self._cache = get_cache_plugin(self.get_option('cache_plugin'), **cache_options) return config
def _expand_hostpattern(self, hostpattern): ''' do some extra checks over normal processing ''' # specification? hostnames, port = super(InventoryModule, self)._expand_hostpattern(hostpattern) if hostpattern.strip().endswith(':') and port is None: raise AssibleParserError( "Invalid host pattern '%s' supplied, ending in ':' is not allowed, this character is reserved to provide a port." % hostpattern) for pattern in hostnames: # some YAML parsing prevention checks if pattern.strip() == '---': raise AssibleParserError( "Invalid host pattern '%s' supplied, '---' is normally a sign this is a YAML file." % hostpattern) return (hostnames, port)
def parse(self, inventory, loader, path, cache=True): ''' parses the inventory file ''' if not HAS_TOML: raise AssibleParserError( 'The TOML inventory plugin requires the python "toml" library' ) super(InventoryModule, self).parse(inventory, loader, path) self.set_options() try: data = self._load_file(path) except Exception as e: raise AssibleParserError(e) if not data: raise AssibleParserError('Parsed empty TOML file') elif data.get('plugin'): raise AssibleParserError('Plugin configuration TOML file, not TOML inventory') for group_name in data: self._parse_group(group_name, data[group_name])
def check_requirements(self): """ Check all requirements for this inventory are satisified""" if not HAS_REQUESTS: raise AssibleParserError( 'Please install "requests" Python module as this is required' ' for VMware Guest dynamic inventory plugin.') elif not HAS_PYVMOMI: raise AssibleParserError( 'Please install "PyVmomi" Python module as this is required' ' for VMware Guest dynamic inventory plugin.') if HAS_REQUESTS: # Pyvmomi 5.5 and onwards requires requests 2.3 # https://github.com/vmware/pyvmomi/blob/master/requirements.txt required_version = (2, 3) requests_version = requests.__version__.split(".")[:2] try: requests_major_minor = tuple(map(int, requests_version)) except ValueError: raise AssibleParserError( "Failed to parse 'requests' library version.") if requests_major_minor < required_version: raise AssibleParserError( "'requests' library version should" " be >= %s, found: %s." % (".".join( [str(w) for w in required_version]), requests.__version__)) if not HAS_VSPHERE and self.with_tags: raise AssibleError( "Unable to find 'vSphere Automation SDK' Python library which is required." " Please refer this URL for installation steps" " - https://code.vmware.com/web/sdk/65/vsphere-automation-python" ) if not all([self.hostname, self.username, self.password]): raise AssibleError( "Missing one of the following : hostname, username, password. Please read " "the documentation for more information.")
def load(data, owner, variable_manager=None, loader=None): ''' Returns a new RoleMetadata object based on the datastructure passed in. ''' if not isinstance(data, dict): raise AssibleParserError( "the 'meta/main.yml' for role %s is not a dictionary" % owner.get_name()) m = RoleMetadata(owner=owner).load_data( data, variable_manager=variable_manager, loader=loader) return m
def parse(self, inventory, loader, path, cache=False): ''' parses the inventory file ''' super(InventoryModule, self).parse(inventory, loader, path, cache=cache) self._read_config_data(path) strict = self.get_option('strict') fact_cache = FactCache() try: # Go over hosts (less var copies) for host in inventory.hosts: # get available variables to templar hostvars = combine_vars( get_group_vars(inventory.hosts[host].get_groups()), inventory.hosts[host].get_vars()) if host in fact_cache: # adds facts if cache is active hostvars = combine_vars(hostvars, fact_cache[host]) # create composite vars self._set_composite_vars(self.get_option('compose'), hostvars, host, strict=strict) # refetch host vars in case new ones have been created above hostvars = combine_vars( get_group_vars(inventory.hosts[host].get_groups()), inventory.hosts[host].get_vars()) if host in self._cache: # adds facts if cache is active hostvars = combine_vars(hostvars, self._cache[host]) # 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) except Exception as e: raise AssibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))