class Base(FieldAttributeBase): _name = FieldAttribute(isa='string', default='', always_post_validate=True, inherit=False) # connection/transport _connection = FieldAttribute( isa='string', default=context.cliargs_deferred_get('connection')) _port = FieldAttribute(isa='int') _remote_user = FieldAttribute( isa='string', default=context.cliargs_deferred_get('remote_user')) # variables _vars = FieldAttribute(isa='dict', priority=100, inherit=False, static=True) # module default params _module_defaults = FieldAttribute(isa='list', extend=True, prepend=True) # flags and misc. settings _environment = FieldAttribute(isa='list', extend=True, prepend=True) _no_log = FieldAttribute(isa='bool') _run_once = FieldAttribute(isa='bool') _ignore_errors = FieldAttribute(isa='bool') _ignore_unreachable = FieldAttribute(isa='bool') _check_mode = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('check')) _diff = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('diff')) _any_errors_fatal = FieldAttribute(isa='bool', default=C.ANY_ERRORS_FATAL) # explicitly invoke a debugger on tasks _debugger = FieldAttribute(isa='string') # param names which have been deprecated/removed DEPRECATED_ATTRIBUTES = [ 'sudo', 'sudo_user', 'sudo_pass', 'sudo_exe', 'sudo_flags', 'su', 'su_user', 'su_pass', 'su_exe', 'su_flags', ]
class Base(FieldAttributeBase): _name = FieldAttribute(isa='string', default='', always_post_validate=True, inherit=False) # connection/transport _connection = FieldAttribute( isa='string', default=context.cliargs_deferred_get('connection')) _port = FieldAttribute(isa='int') _remote_user = FieldAttribute( isa='string', default=context.cliargs_deferred_get('remote_user')) # variables _vars = FieldAttribute(isa='dict', priority=100, inherit=False, static=True) # module default params _module_defaults = FieldAttribute(isa='list', extend=True, prepend=True) # flags and misc. settings _environment = FieldAttribute(isa='list', extend=True, prepend=True) _no_log = FieldAttribute(isa='bool') _run_once = FieldAttribute(isa='bool') _ignore_errors = FieldAttribute(isa='bool') _ignore_unreachable = FieldAttribute(isa='bool') _check_mode = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('check')) _diff = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('diff')) _any_errors_fatal = FieldAttribute(isa='bool', default=C.ANY_ERRORS_FATAL) _throttle = FieldAttribute(isa='int', default=0) _timeout = FieldAttribute(isa='int', default=C.TASK_TIMEOUT) # explicitly invoke a debugger on tasks _debugger = FieldAttribute(isa='string') # Privilege escalation _become = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('become')) _become_method = FieldAttribute( isa='string', default=context.cliargs_deferred_get('become_method')) _become_user = FieldAttribute( isa='string', default=context.cliargs_deferred_get('become_user')) _become_flags = FieldAttribute( isa='string', default=context.cliargs_deferred_get('become_flags')) _become_exe = FieldAttribute( isa='string', default=context.cliargs_deferred_get('become_exe')) # used to hold sudo/su stuff DEPRECATED_ATTRIBUTES = []
class Base(FieldAttributeBase): name = NonInheritableFieldAttribute(isa='string', default='', always_post_validate=True) # connection/transport connection = ConnectionFieldAttribute(isa='string', default=context.cliargs_deferred_get('connection')) port = FieldAttribute(isa='int') remote_user = FieldAttribute(isa='string', default=context.cliargs_deferred_get('remote_user')) # variables vars = NonInheritableFieldAttribute(isa='dict', priority=100, static=True) # module default params module_defaults = FieldAttribute(isa='list', extend=True, prepend=True) # flags and misc. settings environment = FieldAttribute(isa='list', extend=True, prepend=True) no_log = FieldAttribute(isa='bool') run_once = FieldAttribute(isa='bool') ignore_errors = FieldAttribute(isa='bool') ignore_unreachable = FieldAttribute(isa='bool') check_mode = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('check')) diff = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('diff')) any_errors_fatal = FieldAttribute(isa='bool', default=C.ANY_ERRORS_FATAL) throttle = FieldAttribute(isa='int', default=0) timeout = FieldAttribute(isa='int', default=C.TASK_TIMEOUT) # explicitly invoke a debugger on tasks debugger = FieldAttribute(isa='string') # Privilege escalation become = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('become')) become_method = FieldAttribute(isa='string', default=context.cliargs_deferred_get('become_method')) become_user = FieldAttribute(isa='string', default=context.cliargs_deferred_get('become_user')) become_flags = FieldAttribute(isa='string', default=context.cliargs_deferred_get('become_flags')) become_exe = FieldAttribute(isa='string', default=context.cliargs_deferred_get('become_exe')) # used to hold sudo/su stuff DEPRECATED_ATTRIBUTES = [] # type: list[str] def get_path(self): ''' return the absolute path of the playbook object and its line number ''' path = "" try: path = "%s:%s" % (self._ds._data_source, self._ds._line_number) except AttributeError: try: path = "%s:%s" % (self._parent._play._ds._data_source, self._parent._play._ds._line_number) except AttributeError: pass return path def get_dep_chain(self): if hasattr(self, '_parent') and self._parent: return self._parent.get_dep_chain() else: return None def get_search_path(self): ''' Return the list of paths you should search for files, in order. This follows role/playbook dependency chain. ''' path_stack = [] dep_chain = self.get_dep_chain() # inside role: add the dependency chain from current to dependent if dep_chain: path_stack.extend(reversed([x._role_path for x in dep_chain if hasattr(x, '_role_path')])) # add path of task itself, unless it is already in the list task_dir = os.path.dirname(self.get_path()) if task_dir not in path_stack: path_stack.append(task_dir) return path_stack
class Play(Base, Taggable, CollectionSearch): """ A play is a language feature that represents a list of roles and/or task/handler blocks to execute on a given set of hosts. Usage: Play.load(datastructure) -> Play Play.something(...) """ # ================================================================================= _hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) # Facts _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) _gather_subset = FieldAttribute(isa='list', default=(lambda: C.DEFAULT_GATHER_SUBSET), listof=string_types, always_post_validate=True) _gather_timeout = FieldAttribute(isa='int', default=C.DEFAULT_GATHER_TIMEOUT, always_post_validate=True) _fact_path = FieldAttribute(isa='string', default=C.DEFAULT_FACT_PATH) # Variable Attributes _vars_files = FieldAttribute(isa='list', default=list, priority=99) _vars_prompt = FieldAttribute(isa='list', default=list, always_post_validate=False) # Role Attributes _roles = FieldAttribute(isa='list', default=list, priority=90) # Block (Task) Lists Attributes _handlers = FieldAttribute(isa='list', default=list) _pre_tasks = FieldAttribute(isa='list', default=list) _post_tasks = FieldAttribute(isa='list', default=list) _tasks = FieldAttribute(isa='list', default=list) # Flag/Setting Attributes _force_handlers = FieldAttribute( isa='bool', default=context.cliargs_deferred_get('force_handlers'), always_post_validate=True) _max_fail_percentage = FieldAttribute(isa='percent', always_post_validate=True) _serial = FieldAttribute(isa='list', default=list, always_post_validate=True) _strategy = FieldAttribute(isa='string', default=C.DEFAULT_STRATEGY, always_post_validate=True) _order = FieldAttribute(isa='string', always_post_validate=True) # ================================================================================= def __init__(self): super(Play, self).__init__() self._included_conditional = None self._included_path = None self._removed_hosts = [] self.ROLE_CACHE = {} self.only_tags = set(context.CLIARGS.get('tags', [])) or frozenset( ('all', )) self.skip_tags = set(context.CLIARGS.get('skip_tags', [])) def __repr__(self): return self.get_name() def get_name(self): ''' return the name of the Play ''' return self.name @staticmethod def load(data, variable_manager=None, loader=None, vars=None): if ('name' not in data or data['name'] is None) and 'hosts' in data: if data['hosts'] is None or all(host is None for host in data['hosts']): raise AnsibleParserError( "Hosts list cannot be empty - please check your playbook") if isinstance(data['hosts'], list): data['name'] = ','.join(data['hosts']) else: data['name'] = data['hosts'] p = Play() if vars: p.vars = vars.copy() return p.load_data(data, variable_manager=variable_manager, loader=loader) def preprocess_data(self, ds): ''' Adjusts play datastructure to cleanup old/legacy items ''' if not isinstance(ds, dict): raise AnsibleAssertionError( '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 AnsibleParserError( "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 _load_tasks(self, attr, ds): ''' Loads a list of blocks from a list which may be mixed tasks/blocks. Bare tasks outside of a block are given an implicit block. ''' try: return load_list_of_blocks(ds=ds, play=self, variable_manager=self._variable_manager, loader=self._loader) except AssertionError as e: raise AnsibleParserError( "A malformed block was encountered while loading tasks: %s" % to_native(e), obj=self._ds, orig_exc=e) def _load_pre_tasks(self, attr, ds): ''' Loads a list of blocks from a list which may be mixed tasks/blocks. Bare tasks outside of a block are given an implicit block. ''' try: return load_list_of_blocks(ds=ds, play=self, variable_manager=self._variable_manager, loader=self._loader) except AssertionError as e: raise AnsibleParserError( "A malformed block was encountered while loading pre_tasks", obj=self._ds, orig_exc=e) def _load_post_tasks(self, attr, ds): ''' Loads a list of blocks from a list which may be mixed tasks/blocks. Bare tasks outside of a block are given an implicit block. ''' try: return load_list_of_blocks(ds=ds, play=self, variable_manager=self._variable_manager, loader=self._loader) except AssertionError as e: raise AnsibleParserError( "A malformed block was encountered while loading post_tasks", obj=self._ds, orig_exc=e) def _load_handlers(self, attr, ds): ''' Loads a list of blocks from a list which may be mixed handlers/blocks. Bare handlers outside of a block are given an implicit block. ''' try: return self._extend_value( self.handlers, load_list_of_blocks(ds=ds, play=self, use_handlers=True, variable_manager=self._variable_manager, loader=self._loader), prepend=True) except AssertionError as e: raise AnsibleParserError( "A malformed block was encountered while loading handlers", obj=self._ds, orig_exc=e) 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 AnsibleParserError( "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 _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 AnsibleParserError( "Invalid vars_prompt data structure", obj=ds) else: vars_prompts.append(prompt_data) return vars_prompts def _compile_roles(self): ''' Handles the role compilation step, returning a flat list of tasks with the lowest level dependencies first. For example, if a role R has a dependency D1, which also has a dependency D2, the tasks from D2 are merged first, followed by D1, and lastly by the tasks from the parent role R last. This is done for all roles in the Play. ''' block_list = [] if len(self.roles) > 0: for r in self.roles: # Don't insert tasks from ``import/include_role``, preventing # duplicate execution at the wrong time if r.from_include: continue block_list.extend(r.compile(play=self)) return block_list def compile_roles_handlers(self): ''' Handles the role handler compilation step, returning a flat list of Handlers This is done for all roles in the Play. ''' block_list = [] if len(self.roles) > 0: for r in self.roles: if r.from_include: continue block_list.extend(r.get_handler_blocks(play=self)) return block_list def compile(self): ''' Compiles and returns the task list for this play, compiled from the roles (which are themselves compiled recursively) and/or the list of tasks specified in the play. ''' # create a block containing a single flush handlers meta # task, so we can be sure to run handlers at certain points # of the playbook execution flush_block = Block.load(data={'meta': 'flush_handlers'}, play=self, variable_manager=self._variable_manager, loader=self._loader) block_list = [] block_list.extend(self.pre_tasks) block_list.append(flush_block) block_list.extend(self._compile_roles()) block_list.extend(self.tasks) block_list.append(flush_block) block_list.extend(self.post_tasks) block_list.append(flush_block) return block_list def get_vars(self): return self.vars.copy() def get_vars_files(self): if self.vars_files is None: return [] elif not isinstance(self.vars_files, list): return [self.vars_files] return self.vars_files def get_handlers(self): return self.handlers[:] def get_roles(self): return self.roles[:] def get_tasks(self): tasklist = [] for task in self.pre_tasks + self.tasks + self.post_tasks: if isinstance(task, Block): tasklist.append(task.block + task.rescue + task.always) else: tasklist.append(task) return tasklist def serialize(self): data = super(Play, self).serialize() roles = [] for role in self.get_roles(): roles.append(role.serialize()) data['roles'] = roles data['included_path'] = self._included_path return data def deserialize(self, data): super(Play, self).deserialize(data) self._included_path = data.get('included_path', None) if 'roles' in data: role_data = data.get('roles', []) roles = [] for role in role_data: r = Role() r.deserialize(role) roles.append(r) setattr(self, 'roles', roles) del data['roles'] def copy(self): new_me = super(Play, self).copy() new_me.ROLE_CACHE = self.ROLE_CACHE.copy() new_me._included_conditional = self._included_conditional new_me._included_path = self._included_path return new_me
class Become: # Privilege escalation _become = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('become')) _become_method = FieldAttribute( isa='string', default=context.cliargs_deferred_get('become_method')) _become_user = FieldAttribute( isa='string', default=context.cliargs_deferred_get('become_user')) _become_flags = FieldAttribute(isa='string') def __init__(self): super(Become, self).__init__() def _detect_privilege_escalation_conflict(self, ds): # Fail out if user specifies conflicting privilege escalations has_become = 'become' in ds or 'become_user' in ds has_sudo = 'sudo' in ds or 'sudo_user' in ds has_su = 'su' in ds or 'su_user' in ds if has_become: msg = 'The become params ("become", "become_user") and' if has_sudo: raise AnsibleParserError( '%s sudo params ("sudo", "sudo_user") cannot be used together' % msg) elif has_su: raise AnsibleParserError( '%s su params ("su", "su_user") cannot be used together' % msg) elif has_sudo and has_su: raise AnsibleParserError( 'sudo params ("sudo", "sudo_user") and su params ("su", "su_user") cannot be used together' ) def _preprocess_data_become(self, ds): """Preprocess the playbook data for become attributes This is called from the Base object's preprocess_data() method which in turn is called pretty much anytime any sort of playbook object (plays, tasks, blocks, etc) is created. """ self._detect_privilege_escalation_conflict(ds) # Privilege escalation, backwards compatibility for sudo/su if 'sudo' in ds or 'sudo_user' in ds: ds['become_method'] = 'sudo' if 'sudo' in ds: ds['become'] = ds['sudo'] del ds['sudo'] if 'sudo_user' in ds: ds['become_user'] = ds['sudo_user'] del ds['sudo_user'] display.deprecated( "Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default)", '2.9') elif 'su' in ds or 'su_user' in ds: ds['become_method'] = 'su' if 'su' in ds: ds['become'] = ds['su'] del ds['su'] if 'su_user' in ds: ds['become_user'] = ds['su_user'] del ds['su_user'] display.deprecated( "Instead of su/su_user, use become/become_user and set become_method to 'su' (default is sudo)", '2.9') return ds