diff --git a/lib/ansible/playbook/attribute.py b/lib/ansible/playbook/attribute.py index ec243abcd76..35f0f372379 100644 --- a/lib/ansible/playbook/attribute.py +++ b/lib/ansible/playbook/attribute.py @@ -21,7 +21,7 @@ __metaclass__ = type class Attribute: - def __init__(self, isa=None, private=False, default=None, required=False, listof=None, priority=0): + def __init__(self, isa=None, private=False, default=None, required=False, listof=None, priority=0, always_post_validate=False): self.isa = isa self.private = private @@ -29,6 +29,7 @@ class Attribute: self.required = required self.listof = listof self.priority = priority + self.always_post_validate = always_post_validate def __cmp__(self, other): return cmp(other.priority, self.priority) diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index fad1439ff1a..f8d4c91997b 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -48,7 +48,7 @@ class Base: _remote_user = FieldAttribute(isa='string') # variables - _vars = FieldAttribute(isa='dict', default=dict()) + _vars = FieldAttribute(isa='dict', default=dict(), priority=100) # flags and misc. settings _environment = FieldAttribute(isa='list') @@ -266,6 +266,11 @@ class Base: continue else: raise AnsibleParserError("the field '%s' is required but was not set" % name) + elif not attribute.always_post_validate and self.__class__.__name__ not in ('Task', 'PlayContext'): + # Intermediate objects like Play() won't have their fields validated by + # default, as their values are often inherited by other objects and validated + # later, so we don't want them to fail out early + continue try: # Run the post-validator if present. These methods are responsible for diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 342fb5495ae..01847b0a5d5 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -52,22 +52,22 @@ class Play(Base, Taggable, Become): # Connection-Related Attributes # TODO: generalize connection - _accelerate = FieldAttribute(isa='bool', default=False) - _accelerate_ipv6 = FieldAttribute(isa='bool', default=False) - _accelerate_port = FieldAttribute(isa='int', default=5099) # should be alias of port + _accelerate = FieldAttribute(isa='bool', default=False, always_post_validate=True) + _accelerate_ipv6 = FieldAttribute(isa='bool', default=False, always_post_validate=True) + _accelerate_port = FieldAttribute(isa='int', default=5099, always_post_validate=True) # Connection - _gather_facts = FieldAttribute(isa='bool', default=None) - _hosts = FieldAttribute(isa='list', default=[], required=True, listof=string_types) - _name = FieldAttribute(isa='string', default='') + _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) + _hosts = FieldAttribute(isa='list', default=[], required=True, listof=string_types, always_post_validate=True) + _name = FieldAttribute(isa='string', default='', always_post_validate=True) # Variable Attributes - _vars_files = FieldAttribute(isa='list', default=[]) - _vars_prompt = FieldAttribute(isa='list', default=[]) - _vault_password = FieldAttribute(isa='string') + _vars_files = FieldAttribute(isa='list', default=[], priority=99) + _vars_prompt = FieldAttribute(isa='list', default=[], always_post_validate=True) + _vault_password = FieldAttribute(isa='string', always_post_validate=True) # Role Attributes - _roles = FieldAttribute(isa='list', default=[], priority=100) + _roles = FieldAttribute(isa='list', default=[], priority=90) # Block (Task) Lists Attributes _handlers = FieldAttribute(isa='list', default=[]) @@ -76,11 +76,11 @@ class Play(Base, Taggable, Become): _tasks = FieldAttribute(isa='list', default=[]) # Flag/Setting Attributes - _any_errors_fatal = FieldAttribute(isa='bool', default=False) - _force_handlers = FieldAttribute(isa='bool') - _max_fail_percentage = FieldAttribute(isa='percent') - _serial = FieldAttribute(isa='int', default=0) - _strategy = FieldAttribute(isa='string', default='linear') + _any_errors_fatal = FieldAttribute(isa='bool', default=False, always_post_validate=True) + _force_handlers = FieldAttribute(isa='bool', always_post_validate=True) + _max_fail_percentage = FieldAttribute(isa='percent', always_post_validate=True) + _serial = FieldAttribute(isa='int', default=0, always_post_validate=True) + _strategy = FieldAttribute(isa='string', default='linear', always_post_validate=True) # ================================================================================= @@ -191,28 +191,6 @@ class Play(Base, Taggable, Become): roles.append(Role.load(ri, play=self)) return roles - def _post_validate_vars(self, attr, value, templar): - ''' - Override post validation of vars on the play, as we don't want to - template these too early. - ''' - return value - - def _post_validate_vars_files(self, attr, value, templar): - ''' - Override post validation of vars_files on the play, as we don't want to - template these too early. - ''' - return value - - # disable validation on various fields which will be validated later in other objects - def _post_validate_become(self, attr, value, templar): - return value - def _post_validate_become_user(self, attr, value, templar): - return value - def _post_validate_become_method(self, attr, value, templar): - return value - # FIXME: post_validation needs to ensure that become/su/sudo have only 1 set def _compile_roles(self): diff --git a/lib/ansible/playbook/role/definition.py b/lib/ansible/playbook/role/definition.py index a54febe1feb..2e02a723811 100644 --- a/lib/ansible/playbook/role/definition.py +++ b/lib/ansible/playbook/role/definition.py @@ -119,7 +119,7 @@ class RoleDefinition(Base, Become, Conditional, Taggable): # if we have the required datastructures, and if the role_name # contains a variable, try and template it now - if self._play and self._variable_manager: + if self._variable_manager: all_vars = self._variable_manager.get_vars(loader=self._loader, play=self._play) templar = Templar(loader=self._loader, variables=all_vars) if templar._contains_vars(role_name):