def run(self, terms, variables, **kwargs): results = [] for term in terms: try: self.reset() # clear out things for this iteration try: if not self.parse_simple_args(term): self.parse_kv_args(parse_kv(term)) except AssibleError: raise except Exception as e: raise AssibleError( "unknown error parsing with_sequence arguments: %r. Error was: %s" % (term, e)) self.sanity_check() if self.stride != 0: results.extend(self.generate_sequence()) except AssibleError: raise except Exception as e: raise AssibleError("unknown error generating sequence: %s" % e) return results
def load_extra_vars(loader): extra_vars = {} for extra_vars_opt in context.CLIARGS.get('extra_vars', tuple()): data = None extra_vars_opt = to_text(extra_vars_opt, errors='surrogate_or_strict') if extra_vars_opt is None or not extra_vars_opt: continue if extra_vars_opt.startswith(u"@"): # Argument is a YAML file (JSON is a subset of YAML) data = loader.load_from_file(extra_vars_opt[1:]) elif extra_vars_opt[0] in [u'/', u'.']: raise AssibleOptionsError("Please prepend extra_vars filename '%s' with '@'" % extra_vars_opt) elif extra_vars_opt[0] in [u'[', u'{']: # Arguments as YAML data = loader.load(extra_vars_opt) else: # Arguments as Key-value data = parse_kv(extra_vars_opt) if isinstance(data, MutableMapping): extra_vars = combine_vars(extra_vars, data) else: raise AssibleOptionsError("Invalid extra vars data supplied. '%s' could not be made into a dictionary" % extra_vars_opt) return extra_vars
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 _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 _parse_parameters(term): """Hacky parsing of params See https://github.com/assible/assible-modules-core/issues/1968#issuecomment-136842156 and the first_found lookup For how we want to fix this later """ first_split = term.split(' ', 1) if len(first_split) <= 1: # Only a single argument given, therefore it's a path relpath = term params = dict() else: relpath = first_split[0] params = parse_kv(first_split[1]) if '_raw_params' in params: # Spaces in the path? relpath = u' '.join((relpath, params['_raw_params'])) del params['_raw_params'] # Check that we parsed the params correctly if not term.startswith(relpath): # Likely, the user had a non parameter following a parameter. # Reject this as a user typo raise AssibleError( 'Unrecognized value after key=value parameters given to password lookup' ) # No _raw_params means we already found the complete path when # we split it initially # Check for invalid parameters. Probably a user typo invalid_params = frozenset(params.keys()).difference(VALID_PARAMS) if invalid_params: raise AssibleError( 'Unrecognized parameter(s) given to password lookup: %s' % ', '.join(invalid_params)) # Set defaults params['length'] = int(params.get('length', DEFAULT_LENGTH)) params['encrypt'] = params.get('encrypt', None) params['chars'] = params.get('chars', None) if params['chars']: tmp_chars = [] if u',,' in params['chars']: tmp_chars.append(u',') tmp_chars.extend( c for c in params['chars'].replace(u',,', u',').split(u',') if c) params['chars'] = tmp_chars else: # Default chars for password params['chars'] = [u'ascii_letters', u'digits', u".,:-_"] return relpath, params
def _normalize_old_style_args(self, thing): ''' deals with fuzziness in old-style (action/local_action) module invocations returns tuple of (module_name, dictionary_args) possible example inputs: { 'shell' : 'echo hi' } 'shell echo hi' {'module': 'ec2', 'x': 1 } standardized outputs like: ('ec2', { 'x': 1} ) ''' action = None args = None if isinstance(thing, dict): # form is like: action: { module: 'copy', src: 'a', dest: 'b' } thing = thing.copy() if 'module' in thing: action, module_args = self._split_module_string(thing['module']) args = thing.copy() check_raw = action in FREEFORM_ACTIONS args.update(parse_kv(module_args, check_raw=check_raw)) del args['module'] elif isinstance(thing, string_types): # form is like: action: copy src=a dest=b (action, args) = self._split_module_string(thing) check_raw = action in FREEFORM_ACTIONS args = parse_kv(args, check_raw=check_raw) else: # need a dict or a string, so giving up raise AssibleParserError("unexpected parameter type in action: %s" % type(thing), obj=self._task_ds) return (action, args)
def run(self, terms, variables=None, **kwargs): ret = [] for term in terms: kv = parse_kv(term) if '_raw_params' not in kv: raise AssibleError('Search key is required but was not found') key = kv['_raw_params'] paramvals = { 'col': "1", # column to return 'default': None, 'delimiter': "TAB", 'file': 'assible.csv', 'encoding': 'utf-8', } # parameters specified? try: for name, value in kv.items(): if name == '_raw_params': continue if name not in paramvals: raise AssibleAssertionError('%s not in paramvals' % name) paramvals[name] = value except (ValueError, AssertionError) as e: raise AssibleError(e) if paramvals['delimiter'] == 'TAB': paramvals['delimiter'] = "\t" lookupfile = self.find_file_in_search_path(variables, 'files', paramvals['file']) var = self.read_csv(lookupfile, key, paramvals['delimiter'], paramvals['encoding'], paramvals['default'], paramvals['col']) if var is not None: if isinstance(var, MutableSequence): for v in var: ret.append(v) else: ret.append(var) return ret
def _preprocess_import(self, ds, new_ds, k, v): ''' Splits the playbook import line up into filename and parameters ''' if v is None: raise AssibleParserError("playbook import parameter is missing", obj=ds) elif not isinstance(v, string_types): raise AssibleParserError( "playbook import parameter must be a string indicating a file path, got %s instead" % type(v), obj=ds) # The import_playbook line must include at least one item, which is the filename # to import. Anything after that should be regarded as a parameter to the import items = split_args(v) if len(items) == 0: raise AssibleParserError( "import_playbook statements must specify the file name to import", obj=ds) else: new_ds['import_playbook'] = items[0].strip() if len(items) > 1: display.warning( 'Additional parameters in import_playbook statements are not supported. This will be an error in version 2.14' ) # rejoin the parameter portion of the arguments and # then use parse_kv() to get a dict of params back params = parse_kv(" ".join(items[1:])) if 'tags' in params: new_ds['tags'] = params.pop('tags') if 'vars' in new_ds: raise AssibleParserError( "import_playbook parameters cannot be mixed with 'vars' entries for import statements", obj=ds) new_ds['vars'] = params
def _play_ds(self, pattern, async_val, poll): check_raw = context.CLIARGS['module_name'] in C.MODULE_REQUIRE_ARGS mytask = { 'action': { 'module': context.CLIARGS['module_name'], 'args': parse_kv(context.CLIARGS['module_args'], check_raw=check_raw) } } # avoid adding to tasks that don't support it, unless set, then give user an error if context.CLIARGS['module_name'] not in ('include_role', 'include_tasks') and any( frozenset( (async_val, poll))): mytask['async_val'] = async_val mytask['poll'] = poll return dict(name="Assible Ad-Hoc", hosts=pattern, gather_facts='no', tasks=[mytask])
def test_parse_kv(args, expected): assert parse_kv(args) == expected
def default(self, arg, forceshell=False): """ actually runs modules """ if arg.startswith("#"): return False if not self.cwd: display.error("No host found") return False if arg.split()[0] in self.modules: module = arg.split()[0] module_args = ' '.join(arg.split()[1:]) else: module = 'shell' module_args = arg if forceshell is True: module = 'shell' module_args = arg result = None try: check_raw = module in ('command', 'shell', 'script', 'raw') play_ds = dict( name="Assible Shell", hosts=self.cwd, gather_facts='no', tasks=[ dict(action=dict(module=module, args=parse_kv(module_args, check_raw=check_raw))) ], remote_user=self.remote_user, become=self.become, become_user=self.become_user, become_method=self.become_method, check_mode=self.check_mode, diff=self.diff, ) play = Play().load(play_ds, variable_manager=self.variable_manager, loader=self.loader) except Exception as e: display.error(u"Unable to build command: %s" % to_text(e)) return False try: cb = 'minimal' # FIXME: make callbacks configurable # now create a task queue manager to execute the play self._tqm = None try: self._tqm = TaskQueueManager( inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, passwords=self.passwords, stdout_callback=cb, run_additional_callbacks=C.DEFAULT_LOAD_CALLBACK_PLUGINS, run_tree=False, forks=self.forks, ) result = self._tqm.run(play) finally: if self._tqm: self._tqm.cleanup() if self.loader: self.loader.cleanup_all_tmp_files() if result is None: display.error("No hosts found") return False except KeyboardInterrupt: display.error('User interrupted execution') return False except Exception as e: display.error(to_text(e)) # FIXME: add traceback in very very verbose mode return False