@ -33,6 +33,7 @@ from ansible.module_utils._text import to_native
LOOKUP_REGEX = re . compile ( r ' lookup \ s* \ ( ' )
VALID_VAR_REGEX = re . compile ( " ^[_A-Za-z][_a-zA-Z0-9]*$ " )
DEFINED_REGEX = re . compile ( r ' (hostvars \ [.+ \ ]|[ \ w_]+) \ s+(not \ s+is|is|is \ s+not) \ s+(defined|undefined) ' )
class Conditional :
@ -69,6 +70,18 @@ class Conditional:
when = self . _get_parent_attribute ( ' when ' , extend = True , prepend = True )
return when
def extract_defined_undefined ( self , conditional ) :
results = [ ]
cond = conditional
m = DEFINED_REGEX . search ( cond )
while m :
results . append ( m . groups ( ) )
cond = cond [ m . end ( ) : ]
m = DEFINED_REGEX . search ( cond )
return results
def evaluate_conditional ( self , templar , all_vars ) :
'''
Loops through the conditionals set on this object , returning
@ -167,14 +180,31 @@ class Conditional:
else :
raise AnsibleError ( " unable to evaluate conditional: %s " % original )
except ( AnsibleUndefinedVariable , UndefinedError ) as e :
# the templating failed, meaning most likely a
# variable was undefined. If we happened to be
# looking for an undefined variable, return True,
# otherwise fail
if " is undefined " in original or " is not defined " in original or " not is defined " in original :
return True
elif " is defined " in original or " is not undefined " in original or " not is undefined " in original :
return False
else :
# the templating failed, meaning most likely a variable was undefined. If we happened to be
# looking for an undefined variable, return True, otherwise fail
try :
# first we extract the variable name from the error message
var_name = re . compile ( r " ' (hostvars \ [.+ \ ]|[ \ w_]+) ' is undefined " ) . search ( str ( e ) ) . groups ( ) [ 0 ]
# next we extract all defined/undefined tests from the conditional string
def_undef = self . extract_defined_undefined ( conditional )
# then we loop through these, comparing the error variable name against
# each def/undef test we found above. If there is a match, we determine
# whether the logic/state mean the variable should exist or not and return
# the corresponding True/False
for ( du_var , logic , state ) in def_undef :
# when we compare the var names, normalize quotes because something
# like hostvars['foo'] may be tested against hostvars["foo"]
if var_name . replace ( " ' " , ' " ' ) == du_var . replace ( " ' " , ' " ' ) :
# the should exist is a xor test between a negation in the logic portion
# against the state (defined or undefined)
should_exist = ( ' not ' in logic ) != ( state == ' defined ' )
if should_exist :
return False
else :
return True
# as nothing above matched the failed var name, re-raise here to
# trigger the AnsibleUndefinedVariable exception again below
raise
except Exception as new_e :
raise AnsibleUndefinedVariable ( " error while evaluating conditional ( %s ): %s " % ( original , e ) )