|
|
|
@ -28,9 +28,9 @@ import os
|
|
|
|
|
class Play(object):
|
|
|
|
|
|
|
|
|
|
__slots__ = [
|
|
|
|
|
'hosts', 'name', 'vars', 'vars_prompt', 'vars_files',
|
|
|
|
|
'handlers', 'remote_user', 'remote_port', 'accelerate', 'accelerate_port',
|
|
|
|
|
'sudo', 'sudo_user', 'transport', 'playbook',
|
|
|
|
|
'hosts', 'name', 'vars', 'default_vars', 'vars_prompt', 'vars_files',
|
|
|
|
|
'handlers', 'remote_user', 'remote_port', 'included_roles', 'accelerate',
|
|
|
|
|
'accelerate_port', 'sudo', 'sudo_user', 'transport', 'playbook',
|
|
|
|
|
'tags', 'gather_facts', 'serial', '_ds', '_handlers', '_tasks',
|
|
|
|
|
'basedir', 'any_errors_fatal', 'roles', 'max_fail_pct'
|
|
|
|
|
]
|
|
|
|
@ -69,9 +69,18 @@ class Play(object):
|
|
|
|
|
elif type(self.tags) != list:
|
|
|
|
|
self.tags = []
|
|
|
|
|
|
|
|
|
|
ds = self._load_roles(self.roles, ds)
|
|
|
|
|
# We first load the vars files from the datastructure
|
|
|
|
|
# so we have the default variables to pass into the roles
|
|
|
|
|
self.vars_files = ds.get('vars_files', [])
|
|
|
|
|
self._update_vars_files_for_host(None)
|
|
|
|
|
|
|
|
|
|
# now we load the roles into the datastructure
|
|
|
|
|
self.included_roles = []
|
|
|
|
|
ds = self._load_roles(self.roles, ds)
|
|
|
|
|
|
|
|
|
|
# and finally re-process the vars files as they may have
|
|
|
|
|
# been updated by the included roles
|
|
|
|
|
self.vars_files = ds.get('vars_files', [])
|
|
|
|
|
self._update_vars_files_for_host(None)
|
|
|
|
|
|
|
|
|
|
# template everything to be efficient, but do not pre-mature template
|
|
|
|
@ -155,6 +164,17 @@ class Play(object):
|
|
|
|
|
raise errors.AnsibleError("too many levels of recursion while resolving role dependencies")
|
|
|
|
|
for role in roles:
|
|
|
|
|
role_path,role_vars = self._get_role_path(role)
|
|
|
|
|
role_vars = utils.combine_vars(role_vars, passed_vars)
|
|
|
|
|
vars = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(role_path, 'vars')))
|
|
|
|
|
vars_data = {}
|
|
|
|
|
if os.path.isfile(vars):
|
|
|
|
|
vars_data = utils.parse_yaml_from_file(vars)
|
|
|
|
|
if vars_data:
|
|
|
|
|
role_vars = utils.combine_vars(vars_data, role_vars)
|
|
|
|
|
defaults = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(role_path, 'defaults')))
|
|
|
|
|
defaults_data = {}
|
|
|
|
|
if os.path.isfile(defaults):
|
|
|
|
|
defaults_data = utils.parse_yaml_from_file(defaults)
|
|
|
|
|
# the meta directory contains the yaml that should
|
|
|
|
|
# hold the list of dependencies (if any)
|
|
|
|
|
meta = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(role_path, 'meta')))
|
|
|
|
@ -164,27 +184,52 @@ class Play(object):
|
|
|
|
|
dependencies = data.get('dependencies',[])
|
|
|
|
|
for dep in dependencies:
|
|
|
|
|
(dep_path,dep_vars) = self._get_role_path(dep)
|
|
|
|
|
meta = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(dep_path, 'meta')))
|
|
|
|
|
if os.path.isfile(meta):
|
|
|
|
|
meta_data = utils.parse_yaml_from_file(meta)
|
|
|
|
|
if meta_data:
|
|
|
|
|
allow_dupes = utils.boolean(meta_data.get('allow_duplicates',''))
|
|
|
|
|
if not allow_dupes:
|
|
|
|
|
if dep in self.included_roles:
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
self.included_roles.append(dep)
|
|
|
|
|
dep_vars = utils.combine_vars(passed_vars, dep_vars)
|
|
|
|
|
dep_vars = utils.combine_vars(role_vars, dep_vars)
|
|
|
|
|
vars = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(dep_path, 'vars')))
|
|
|
|
|
vars_data = {}
|
|
|
|
|
if os.path.isfile(vars):
|
|
|
|
|
vars_data = utils.parse_yaml_from_file(vars)
|
|
|
|
|
dep_vars.update(role_vars)
|
|
|
|
|
for k in passed_vars.keys():
|
|
|
|
|
if not k in dep_vars:
|
|
|
|
|
dep_vars[k] = passed_vars[k]
|
|
|
|
|
for k in vars_data.keys():
|
|
|
|
|
if not k in dep_vars:
|
|
|
|
|
dep_vars[k] = vars_data[k]
|
|
|
|
|
if vars_data:
|
|
|
|
|
dep_vars = utils.combine_vars(vars_data, dep_vars)
|
|
|
|
|
defaults = self._resolve_main(utils.path_dwim(self.basedir, os.path.join(dep_path, 'defaults')))
|
|
|
|
|
dep_defaults_data = {}
|
|
|
|
|
if os.path.isfile(defaults):
|
|
|
|
|
dep_defaults_data = utils.parse_yaml_from_file(defaults)
|
|
|
|
|
if 'role' in dep_vars:
|
|
|
|
|
del dep_vars['role']
|
|
|
|
|
self._build_role_dependencies([dep], dep_stack, passed_vars=dep_vars, level=level+1)
|
|
|
|
|
dep_stack.append([dep,dep_path,dep_vars])
|
|
|
|
|
dep_stack.append([dep,dep_path,dep_vars,dep_defaults_data])
|
|
|
|
|
# only add the current role when we're at the top level,
|
|
|
|
|
# otherwise we'll end up in a recursive loop
|
|
|
|
|
if level == 0:
|
|
|
|
|
dep_stack.append([role,role_path,role_vars])
|
|
|
|
|
dep_stack.append([role,role_path,role_vars,defaults_data])
|
|
|
|
|
return dep_stack
|
|
|
|
|
|
|
|
|
|
def _load_role_defaults(self, defaults_files):
|
|
|
|
|
# process default variables
|
|
|
|
|
default_vars = {}
|
|
|
|
|
for filename in defaults_files:
|
|
|
|
|
if os.path.exists(filename):
|
|
|
|
|
new_default_vars = utils.parse_yaml_from_file(filename)
|
|
|
|
|
if new_default_vars:
|
|
|
|
|
if type(new_default_vars) != dict:
|
|
|
|
|
raise errors.AnsibleError("%s must be stored as dictonary/hash: %s" % (filename, type(new_default_vars)))
|
|
|
|
|
|
|
|
|
|
default_vars = utils.combine_vars(default_vars, new_default_vars)
|
|
|
|
|
|
|
|
|
|
return default_vars
|
|
|
|
|
|
|
|
|
|
def _load_roles(self, roles, ds):
|
|
|
|
|
# a role is a name that auto-includes the following if they exist
|
|
|
|
|
# <rolename>/tasks/main.yml
|
|
|
|
@ -201,6 +246,7 @@ class Play(object):
|
|
|
|
|
new_tasks = []
|
|
|
|
|
new_handlers = []
|
|
|
|
|
new_vars_files = []
|
|
|
|
|
defaults_files = []
|
|
|
|
|
|
|
|
|
|
pre_tasks = ds.get('pre_tasks', None)
|
|
|
|
|
if type(pre_tasks) != list:
|
|
|
|
@ -213,7 +259,7 @@ class Play(object):
|
|
|
|
|
|
|
|
|
|
roles = self._build_role_dependencies(roles, [], self.vars)
|
|
|
|
|
|
|
|
|
|
for role,role_path,role_vars in roles:
|
|
|
|
|
for (role,role_path,role_vars,default_vars) in roles:
|
|
|
|
|
# special vars must be extracted from the dict to the included tasks
|
|
|
|
|
special_keys = [ "sudo", "sudo_user", "when", "with_items" ]
|
|
|
|
|
special_vars = {}
|
|
|
|
@ -224,16 +270,19 @@ class Play(object):
|
|
|
|
|
task_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'tasks'))
|
|
|
|
|
handler_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'handlers'))
|
|
|
|
|
vars_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'vars'))
|
|
|
|
|
defaults_basepath = utils.path_dwim(self.basedir, os.path.join(role_path, 'defaults'))
|
|
|
|
|
|
|
|
|
|
task = self._resolve_main(task_basepath)
|
|
|
|
|
handler = self._resolve_main(handler_basepath)
|
|
|
|
|
vars_file = self._resolve_main(vars_basepath)
|
|
|
|
|
defaults_file = self._resolve_main(defaults_basepath)
|
|
|
|
|
|
|
|
|
|
library = utils.path_dwim(self.basedir, os.path.join(role_path, 'library'))
|
|
|
|
|
|
|
|
|
|
if not os.path.isfile(task) and not os.path.isfile(handler) and not os.path.isfile(vars_file) and not os.path.isdir(library):
|
|
|
|
|
raise errors.AnsibleError("found role at %s, but cannot find %s or %s or %s or %s" % (role_path, task, handler, vars_file, library))
|
|
|
|
|
if os.path.isfile(task):
|
|
|
|
|
nt = dict(include=pipes.quote(task), vars=role_vars)
|
|
|
|
|
nt = dict(include=pipes.quote(task), vars=role_vars, default_vars=default_vars)
|
|
|
|
|
for k in special_keys:
|
|
|
|
|
if k in special_vars:
|
|
|
|
|
nt[k] = special_vars[k]
|
|
|
|
@ -246,6 +295,8 @@ class Play(object):
|
|
|
|
|
new_handlers.append(nt)
|
|
|
|
|
if os.path.isfile(vars_file):
|
|
|
|
|
new_vars_files.append(vars_file)
|
|
|
|
|
if os.path.isfile(defaults_file):
|
|
|
|
|
defaults_files.append(defaults_file)
|
|
|
|
|
if os.path.isdir(library):
|
|
|
|
|
utils.plugins.module_finder.add_directory(library)
|
|
|
|
|
|
|
|
|
@ -277,6 +328,8 @@ class Play(object):
|
|
|
|
|
ds['handlers'] = new_handlers
|
|
|
|
|
ds['vars_files'] = new_vars_files
|
|
|
|
|
|
|
|
|
|
self.default_vars = self._load_role_defaults(defaults_files)
|
|
|
|
|
|
|
|
|
|
return ds
|
|
|
|
|
|
|
|
|
|
# *************************************************
|
|
|
|
@ -299,7 +352,7 @@ class Play(object):
|
|
|
|
|
|
|
|
|
|
# *************************************************
|
|
|
|
|
|
|
|
|
|
def _load_tasks(self, tasks, vars={}, sudo_vars={}, additional_conditions=[], original_file=None):
|
|
|
|
|
def _load_tasks(self, tasks, vars={}, default_vars={}, sudo_vars={}, additional_conditions=[], original_file=None):
|
|
|
|
|
''' handle task and handler include statements '''
|
|
|
|
|
|
|
|
|
|
results = []
|
|
|
|
@ -345,11 +398,12 @@ class Play(object):
|
|
|
|
|
included_additional_conditions.insert(0, utils.compile_when_to_only_if("%s %s" % (k[5:], x[k])))
|
|
|
|
|
elif k == 'when':
|
|
|
|
|
included_additional_conditions.insert(0, utils.compile_when_to_only_if("jinja2_compare %s" % x[k]))
|
|
|
|
|
elif k in ("include", "vars", "only_if", "sudo", "sudo_user"):
|
|
|
|
|
elif k in ("include", "vars", "default_vars", "only_if", "sudo", "sudo_user"):
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
raise errors.AnsibleError("parse error: task includes cannot be used with other directives: %s" % k)
|
|
|
|
|
|
|
|
|
|
default_vars = utils.combine_vars(self.default_vars, x.get('default_vars', {}))
|
|
|
|
|
if 'vars' in x:
|
|
|
|
|
task_vars = utils.combine_vars(task_vars, x['vars'])
|
|
|
|
|
if 'only_if' in x:
|
|
|
|
@ -367,9 +421,9 @@ class Play(object):
|
|
|
|
|
include_file = template(dirname, tokens[0], mv)
|
|
|
|
|
include_filename = utils.path_dwim(dirname, include_file)
|
|
|
|
|
data = utils.parse_yaml_from_file(include_filename)
|
|
|
|
|
results += self._load_tasks(data, mv, included_sudo_vars, included_additional_conditions, original_file=include_filename)
|
|
|
|
|
results += self._load_tasks(data, mv, default_vars, included_sudo_vars, included_additional_conditions, original_file=include_filename)
|
|
|
|
|
elif type(x) == dict:
|
|
|
|
|
results.append(Task(self,x,module_vars=task_vars, additional_conditions=additional_conditions))
|
|
|
|
|
results.append(Task(self,x,module_vars=task_vars,default_vars=default_vars,additional_conditions=additional_conditions))
|
|
|
|
|
else:
|
|
|
|
|
raise Exception("unexpected task type")
|
|
|
|
|
|
|
|
|
|