Make v2 playbook class attributes inheritable

Also fixing some other become-related things
pull/10488/merge
James Cammarata 10 years ago
parent 8d8c4c0615
commit 393246fdd3

@ -157,13 +157,10 @@ class ConnectionInformation:
new_info.copy(self) new_info.copy(self)
for attr in ('connection', 'remote_user', 'become', 'become_user', 'become_pass', 'become_method', 'environment', 'no_log'): for attr in ('connection', 'remote_user', 'become', 'become_user', 'become_pass', 'become_method', 'environment', 'no_log'):
attr_val = None
if hasattr(task, attr): if hasattr(task, attr):
attr_val = getattr(task, attr) attr_val = getattr(task, attr)
if task._block and hasattr(task._block, attr) and not attr_val: if attr_val:
attr_val = getattr(task._block, attr) setattr(new_info, attr, attr_val)
if attr_val:
setattr(new_info, attr, attr_val)
return new_info return new_info
@ -184,6 +181,7 @@ class ConnectionInformation:
executable = executable or '$SHELL' executable = executable or '$SHELL'
success_cmd = pipes.quote('echo %s; %s' % (success_key, cmd))
if self.become: if self.become:
if self.become_method == 'sudo': if self.become_method == 'sudo':
# Rather than detect if sudo wants a password this time, -k makes sudo always ask for # Rather than detect if sudo wants a password this time, -k makes sudo always ask for
@ -195,23 +193,23 @@ class ConnectionInformation:
exe = become_settings.get('sudo_exe', C.DEFAULT_SUDO_EXE) exe = become_settings.get('sudo_exe', C.DEFAULT_SUDO_EXE)
flags = become_settings.get('sudo_flags', C.DEFAULT_SUDO_FLAGS) flags = become_settings.get('sudo_flags', C.DEFAULT_SUDO_FLAGS)
becomecmd = '%s -k && %s %s -S -p "%s" -u %s %s -c %s' % \ becomecmd = '%s -k && %s %s -S -p "%s" -u %s %s -c %s' % \
(exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, self.become_user, executable, pipes.quote('echo %s; %s' % (success_key, cmd))) (exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, self.become_user, executable, success_cmd)
elif self.become_method == 'su': elif self.become_method == 'su':
exe = become_settings.get('su_exe', C.DEFAULT_SU_EXE) exe = become_settings.get('su_exe', C.DEFAULT_SU_EXE)
flags = become_settings.get('su_flags', C.DEFAULT_SU_FLAGS) flags = become_settings.get('su_flags', C.DEFAULT_SU_FLAGS)
becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, self.become_user, executable, pipes.quote('echo %s; %s' % (success_key, cmd))) becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, self.become_user, executable, success_cmd)
elif self.become_method == 'pbrun': elif self.become_method == 'pbrun':
exe = become_settings.get('pbrun_exe', 'pbrun') exe = become_settings.get('pbrun_exe', 'pbrun')
flags = become_settings.get('pbrun_flags', '') flags = become_settings.get('pbrun_flags', '')
becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, user, pipes.quote('echo %s; %s' % (success_key, cmd))) becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, user, success_cmd)
elif self.become_method == 'pfexec': elif self.become_method == 'pfexec':
exe = become_settings.get('pfexec_exe', 'pbrun') exe = become_settings.get('pfexec_exe', 'pbrun')
flags = become_settings.get('pfexec_flags', '') flags = become_settings.get('pfexec_flags', '')
# No user as it uses it's own exec_attr to figure it out # No user as it uses it's own exec_attr to figure it out
becomecmd = '%s %s "%s"' % (exe, flags, pipes.quote('echo %s; %s' % (success_key, cmd))) becomecmd = '%s %s "%s"' % (exe, flags, success_cmd)
else: else:
raise errors.AnsibleError("Privilege escalation method not found: %s" % method) raise errors.AnsibleError("Privilege escalation method not found: %s" % method)

@ -72,11 +72,20 @@ class Base:
def munge(self, ds): def munge(self, ds):
''' infrequently used method to do some pre-processing of legacy terms ''' ''' infrequently used method to do some pre-processing of legacy terms '''
def _get_base_classes_munge(target_class):
base_classes = list(target_class.__bases__[:])
for base_class in target_class.__bases__:
base_classes.extend( _get_base_classes_munge(base_class))
return base_classes
base_classes = list(self.__class__.__bases__[:])
for base_class in self.__class__.__bases__: for base_class in self.__class__.__bases__:
method = getattr(self, ("_munge_%s" % base_class.__name__).lower(), None) base_classes.extend(_get_base_classes_munge(base_class))
if method:
ds = method(ds)
for base_class in base_classes:
method = getattr(self, "_munge_%s" % base_class.__name__.lower(), None)
if method:
return method(ds)
return ds return ds
def load_data(self, ds, variable_manager=None, loader=None): def load_data(self, ds, variable_manager=None, loader=None):
@ -271,15 +280,21 @@ class Base:
# optionally allowing masking by accessors # optionally allowing masking by accessors
if not needle.startswith("_"): if not needle.startswith("_"):
method = "get_%s" % needle method = "_get_attr_%s" % needle
if method in self.__dict__: if method in dir(self):
return method(self) return getattr(self, method)()
if needle in self._attributes: if needle in self._attributes:
return self._attributes[needle] return self._attributes[needle]
raise AttributeError("attribute not found in %s: %s" % (self.__class__.__name__, needle)) raise AttributeError("attribute not found in %s: %s" % (self.__class__.__name__, needle))
def __setattr__(self, needle, value):
if hasattr(self, '_attributes') and needle in self._attributes:
self._attributes[needle] = value
else:
super(Base, self).__setattr__(needle, value)
def __getstate__(self): def __getstate__(self):
return self.serialize() return self.serialize()

@ -95,3 +95,41 @@ class Become:
ds['become_user'] = C.DEFAULT_BECOME_USER ds['become_user'] = C.DEFAULT_BECOME_USER
return ds return ds
def _get_attr_become(self):
'''
Override for the 'become' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become')
else:
return self._attributes['become']
def _get_attr_become_method(self):
'''
Override for the 'become_method' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become_method')
else:
return self._attributes['become_method']
def _get_attr_become_user(self):
'''
Override for the 'become_user' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become_user')
else:
return self._attributes['become_user']
def _get_attr_become_password(self):
'''
Override for the 'become_password' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become_password')
else:
return self._attributes['become_password']

@ -131,23 +131,24 @@ class Block(Base, Become, Conditional, Taggable):
# use_handlers=self._use_handlers, # use_handlers=self._use_handlers,
# ) # )
def compile(self):
'''
Returns the task list for this object
'''
task_list = []
for task in self.block:
# FIXME: evaulate task tags/conditionals here
task_list.extend(task.compile())
return task_list
def copy(self): def copy(self):
def _dupe_task_list(task_list, new_block):
new_task_list = []
for task in task_list:
new_task = task.copy(exclude_block=True)
new_task._block = new_block
new_task_list.append(new_task)
return new_task_list
new_me = super(Block, self).copy() new_me = super(Block, self).copy()
new_me._use_handlers = self._use_handlers new_me._use_handlers = self._use_handlers
new_me._dep_chain = self._dep_chain[:] new_me._dep_chain = self._dep_chain[:]
new_me.block = _dupe_task_list(self.block or [], new_me)
new_me.rescue = _dupe_task_list(self.rescue or [], new_me)
new_me.always = _dupe_task_list(self.always or [], new_me)
print("new block tasks are: %s" % new_me.block)
new_me._parent_block = None new_me._parent_block = None
if self._parent_block: if self._parent_block:
new_me._parent_block = self._parent_block.copy() new_me._parent_block = self._parent_block.copy()
@ -252,3 +253,24 @@ class Block(Base, Become, Conditional, Taggable):
for dep in self._dep_chain: for dep in self._dep_chain:
dep.set_loader(loader) dep.set_loader(loader)
def _get_parent_attribute(self, attr):
'''
Generic logic to get the attribute or parent attribute for a block value.
'''
value = self._attributes[attr]
if not value:
if self._parent_block:
value = getattr(self._block, attr)
elif self._role:
value = getattr(self._role, attr)
if not value and len(self._dep_chain):
reverse_dep_chain = self._dep_chain[:]
reverse_dep_chain.reverse()
for dep in reverse_dep_chain:
value = getattr(dep, attr)
if value:
break
return value

@ -37,6 +37,7 @@ def load_list_of_blocks(ds, parent_block=None, role=None, task_include=None, use
assert type(ds) in (list, NoneType) assert type(ds) in (list, NoneType)
block_list = [] block_list = []
print("in load list of blocks, ds is: %s" % ds)
if ds: if ds:
for block in ds: for block in ds:
b = Block.load( b = Block.load(
@ -50,6 +51,7 @@ def load_list_of_blocks(ds, parent_block=None, role=None, task_include=None, use
) )
block_list.append(b) block_list.append(b)
print("-> returning block list: %s" % block_list)
return block_list return block_list

@ -219,6 +219,7 @@ class Play(Base, Taggable, Become):
block_list.extend(self.tasks) block_list.extend(self.tasks)
block_list.extend(self.post_tasks) block_list.extend(self.post_tasks)
print("block list is: %s" % block_list)
return block_list return block_list
def get_vars(self): def get_vars(self):

@ -30,6 +30,7 @@ from ansible.errors import AnsibleError, AnsibleParserError
from ansible.parsing import DataLoader from ansible.parsing import DataLoader
from ansible.playbook.attribute import FieldAttribute from ansible.playbook.attribute import FieldAttribute
from ansible.playbook.base import Base from ansible.playbook.base import Base
from ansible.playbook.become import Become
from ansible.playbook.conditional import Conditional from ansible.playbook.conditional import Conditional
from ansible.playbook.helpers import load_list_of_blocks, compile_block_list from ansible.playbook.helpers import load_list_of_blocks, compile_block_list
from ansible.playbook.role.include import RoleInclude from ansible.playbook.role.include import RoleInclude
@ -69,7 +70,7 @@ def hash_params(params):
ROLE_CACHE = dict() ROLE_CACHE = dict()
class Role(Base, Conditional, Taggable): class Role(Base, Become, Conditional, Taggable):
def __init__(self): def __init__(self):
self._role_name = None self._role_name = None
@ -136,6 +137,12 @@ class Role(Base, Conditional, Taggable):
if parent_role: if parent_role:
self.add_parent(parent_role) self.add_parent(parent_role)
# copy over all field attributes, except for when and tags, which
# are special cases and need to preserve pre-existing values
for (attr_name, _) in iteritems(self._get_base_attributes()):
if attr_name not in ('when', 'tags'):
setattr(self, attr_name, getattr(role_include, attr_name))
current_when = getattr(self, 'when')[:] current_when = getattr(self, 'when')[:]
current_when.extend(role_include.when) current_when.extend(role_include.when)
setattr(self, 'when', current_when) setattr(self, 'when', current_when)
@ -144,10 +151,6 @@ class Role(Base, Conditional, Taggable):
current_tags.extend(role_include.tags) current_tags.extend(role_include.tags)
setattr(self, 'tags', current_tags) setattr(self, 'tags', current_tags)
# save the current base directory for the loader and set it to the current role path
#cur_basedir = self._loader.get_basedir()
#self._loader.set_basedir(self._role_path)
# load the role's files, if they exist # load the role's files, if they exist
library = os.path.join(self._role_path, 'library') library = os.path.join(self._role_path, 'library')
if os.path.isdir(library): if os.path.isdir(library):
@ -179,9 +182,6 @@ class Role(Base, Conditional, Taggable):
elif self._default_vars is None: elif self._default_vars is None:
self._default_vars = dict() self._default_vars = dict()
# and finally restore the previous base directory
#self._loader.set_basedir(cur_basedir)
def _load_role_yaml(self, subdir): def _load_role_yaml(self, subdir):
file_path = os.path.join(self._role_path, subdir) file_path = os.path.join(self._role_path, subdir)
if self._loader.path_exists(file_path) and self._loader.is_directory(file_path): if self._loader.path_exists(file_path) and self._loader.is_directory(file_path):
@ -313,9 +313,6 @@ class Role(Base, Conditional, Taggable):
for dep in deps: for dep in deps:
dep_blocks = dep.compile(dep_chain=new_dep_chain) dep_blocks = dep.compile(dep_chain=new_dep_chain)
for dep_block in dep_blocks: for dep_block in dep_blocks:
# since we're modifying the task, and need it to be unique,
# we make a copy of it here and assign the dependency chain
# to the copy, then append the copy to the task list.
new_dep_block = dep_block.copy() new_dep_block = dep_block.copy()
new_dep_block._dep_chain = new_dep_chain new_dep_block._dep_chain = new_dep_chain
block_list.append(new_dep_block) block_list.append(new_dep_block)

@ -28,6 +28,7 @@ from ansible.errors import AnsibleError
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleMapping from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleMapping
from ansible.playbook.attribute import Attribute, FieldAttribute from ansible.playbook.attribute import Attribute, FieldAttribute
from ansible.playbook.base import Base from ansible.playbook.base import Base
from ansible.playbook.become import Become
from ansible.playbook.conditional import Conditional from ansible.playbook.conditional import Conditional
from ansible.playbook.taggable import Taggable from ansible.playbook.taggable import Taggable
from ansible.utils.path import unfrackpath from ansible.utils.path import unfrackpath
@ -36,7 +37,7 @@ from ansible.utils.path import unfrackpath
__all__ = ['RoleDefinition'] __all__ = ['RoleDefinition']
class RoleDefinition(Base, Conditional, Taggable): class RoleDefinition(Base, Become, Conditional, Taggable):
_role = FieldAttribute(isa='string') _role = FieldAttribute(isa='string')
@ -57,6 +58,9 @@ class RoleDefinition(Base, Conditional, Taggable):
assert isinstance(ds, dict) or isinstance(ds, string_types) assert isinstance(ds, dict) or isinstance(ds, string_types)
if isinstance(ds, dict):
ds = super(RoleDefinition, self).munge(ds)
# we create a new data structure here, using the same # we create a new data structure here, using the same
# object used internally by the YAML parsing code so we # object used internally by the YAML parsing code so we
# can preserve file:line:column information if it exists # can preserve file:line:column information if it exists
@ -88,7 +92,7 @@ class RoleDefinition(Base, Conditional, Taggable):
self._ds = ds self._ds = ds
# and return the cleaned-up data structure # and return the cleaned-up data structure
return super(RoleDefinition, self).munge(new_ds) return new_ds
def _load_role_name(self, ds): def _load_role_name(self, ds):
''' '''

@ -210,20 +210,21 @@ class Task(Base, Conditional, Taggable, Become):
del all_vars['when'] del all_vars['when']
return all_vars return all_vars
def compile(self): # no longer used, as blocks are the lowest level of compilation now
''' #def compile(self):
For tasks, this is just a dummy method returning an array # '''
with 'self' in it, so we don't have to care about task types # For tasks, this is just a dummy method returning an array
further up the chain. # with 'self' in it, so we don't have to care about task types
''' # further up the chain.
# '''
return [self] #
# return [self]
def copy(self):
def copy(self, exclude_block=False):
new_me = super(Task, self).copy() new_me = super(Task, self).copy()
new_me._block = None new_me._block = None
if self._block: if self._block and not exclude_block:
new_me._block = self._block.copy() new_me._block = self._block.copy()
new_me._role = None new_me._role = None
@ -309,3 +310,12 @@ class Task(Base, Conditional, Taggable, Become):
if self._task_include: if self._task_include:
self._task_include.set_loader(loader) self._task_include.set_loader(loader)
def _get_parent_attribute(self, attr):
'''
Generic logic to get the attribute or parent attribute for a task value.
'''
value = self._attributes[attr]
if not value and self._block:
value = getattr(self._block, attr)
return value

@ -0,0 +1,2 @@
- debug: msg="this is test_become_r1"
- command: whoami

@ -0,0 +1,3 @@
allow_duplicates: yes
dependencies:
- test_become_r1

@ -0,0 +1,2 @@
- debug: msg="this is test_become_r2"
- command: whoami

@ -1,8 +1,14 @@
- hosts: all - hosts: all
gather_facts: no gather_facts: no
roles:
- { role: test_become_r2 }
- { role: test_become_r2, sudo_user: testing }
tasks: tasks:
- command: whoami
- command: whoami - command: whoami
become_user: testing become_user: testing
- block:
- command: whoami
- block: - block:
- command: whoami - command: whoami
become_user: testing become_user: testing

Loading…
Cancel
Save