mirror of https://github.com/ansible/ansible.git
Playbook refactoring -- work in progress.
parent
0f6a18f2e2
commit
fb261f94b7
@ -0,0 +1,176 @@
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#############################################
|
||||
|
||||
from ansible import utils
|
||||
from ansible import errors
|
||||
from ansible.playbook.task import Task
|
||||
import shlex
|
||||
import os
|
||||
|
||||
class Play(object):
|
||||
|
||||
__slots__ = [
|
||||
'hosts', 'name', 'vars', 'vars_prompt', 'vars_files', 'handlers', 'remote_user', 'remote_port',
|
||||
'sudo', 'sudo_user', 'transport', 'playbook', '_ds', '_handlers', '_tasks'
|
||||
]
|
||||
|
||||
# *************************************************
|
||||
|
||||
def __init__(self, playbook, ds):
|
||||
|
||||
self._ds = ds
|
||||
self.playbook = playbook
|
||||
self.hosts = ds.get('hosts', None)
|
||||
self.hosts = utils.template(self.hosts, self.playbook.extra_vars, {})
|
||||
self.name = ds.get('name', self.hosts)
|
||||
self.vars = ds.get('vars', {})
|
||||
self.vars_files = ds.get('vars_files', [])
|
||||
self.vars_prompt = ds.get('vars_prompt', {})
|
||||
self.vars = self._get_vars(self.playbook.basedir)
|
||||
self._tasks = ds.get('tasks', [])
|
||||
self._handlers = ds.get('handlers', [])
|
||||
self.remote_user = ds.get('user', self.playbook.remote_user)
|
||||
self.remote_port = ds.get('port', self.playbook.remote_port)
|
||||
self.sudo = ds.get('sudo', self.playbook.sudo)
|
||||
self.sudo_user = ds.get('sudo_user', self.playbook.sudo_user)
|
||||
self.transport = ds.get('connection', self.playbook.transport)
|
||||
self._tasks = self._load_tasks(self._ds, 'tasks')
|
||||
self._handlers = self._load_tasks(self._ds, 'handlers')
|
||||
|
||||
if self.hosts is None:
|
||||
raise errors.AnsibleError('hosts declaration is required')
|
||||
if isinstance(self.hosts, list):
|
||||
self.hosts = ';'.join(self.hosts)
|
||||
if self.sudo_user != 'root':
|
||||
self.sudo = True
|
||||
|
||||
# *************************************************
|
||||
|
||||
def _load_tasks(self, ds, keyname):
|
||||
''' handle task and handler include statements '''
|
||||
|
||||
items = ds.get(keyname, [])
|
||||
results = []
|
||||
for x in items:
|
||||
if 'include' in x:
|
||||
task_vars = self.vars.copy()
|
||||
tokens = shlex.split(x['include'])
|
||||
for t in tokens[1:]:
|
||||
(k,v) = t.split("=", 1)
|
||||
task_vars[k]=v
|
||||
include_file = tokens[0]
|
||||
data = utils.parse_yaml_from_file(utils.path_dwim(self.playbook.basedir, include_file))
|
||||
for y in data:
|
||||
t = Task(self,y)
|
||||
# TODO: rename this to just 'vars'
|
||||
t.module_vars = self.vars.copy()
|
||||
t.module_vars.update(task_vars)
|
||||
results.append(t)
|
||||
elif type(x) == dict:
|
||||
results.append(Task(self, x))
|
||||
else:
|
||||
raise Exception("unexpected task type")
|
||||
return results
|
||||
|
||||
# *************************************************
|
||||
|
||||
def handlers(self):
|
||||
return self._handlers
|
||||
|
||||
def tasks(self):
|
||||
return self._tasks
|
||||
|
||||
# *************************************************
|
||||
|
||||
def _get_vars(self, dirname):
|
||||
''' load the vars section from a play '''
|
||||
|
||||
if self.vars is None:
|
||||
self.vars = {}
|
||||
|
||||
if type(self.vars) not in [dict, list]:
|
||||
raise errors.AnsibleError("'vars' section must contain only key/value pairs")
|
||||
|
||||
vars = self.playbook.global_vars
|
||||
|
||||
# translate a list of vars into a dict
|
||||
if type(self.vars) == list:
|
||||
for item in self.vars:
|
||||
k, v = item.items()[0]
|
||||
vars[k] = v
|
||||
else:
|
||||
vars.update(self.vars)
|
||||
|
||||
if type(self.vars_prompt) != dict:
|
||||
raise errors.AnsibleError("'vars_prompt' section must contain only key/value pairs")
|
||||
for vname in self.vars_prompt:
|
||||
# TODO: make this prompt one line and consider double entry
|
||||
vars[vname] = self.callbacks.on_vars_prompt(vname)
|
||||
|
||||
results = self.playbook.extra_vars.copy()
|
||||
results.update(vars)
|
||||
return results
|
||||
|
||||
# *************************************************
|
||||
|
||||
def update_vars_files(self, hosts):
|
||||
''' calculate vars_files, which requires that setup runs first so ansible facts can be mixed in '''
|
||||
for h in hosts:
|
||||
self.update_vars_files_for_host(h)
|
||||
|
||||
# *************************************************
|
||||
|
||||
def update_vars_files_for_host(self, host):
|
||||
|
||||
if not host in self.playbook.SETUP_CACHE:
|
||||
# no need to process failed hosts or hosts not in this play
|
||||
return
|
||||
|
||||
for filename in self.vars_files:
|
||||
# TODO: maybe have to template the path here...
|
||||
|
||||
if type(filename) == list:
|
||||
|
||||
# loop over all filenames, loading the first one, and failing if # none found
|
||||
found = False
|
||||
sequence = []
|
||||
for real_filename in filename:
|
||||
filename2 = utils.template(real_filename, self.playbook.SETUP_CACHE[host])
|
||||
filename2 = utils.template(filename2, self.vars)
|
||||
filename2 = utils.path_dwim(self.playbook.basedir, filename2)
|
||||
sequence.append(filename2)
|
||||
if os.path.exists(filename2):
|
||||
found = True
|
||||
data = utils.parse_yaml_from_file(filename2)
|
||||
self.playbook.SETUP_CACHE[host].update(data)
|
||||
self.playbook.callbacks.on_import_for_host(host, filename2)
|
||||
break
|
||||
else:
|
||||
self.playbook.callbacks.on_not_import_for_host(host, filename2)
|
||||
if not found:
|
||||
raise errors.AnsibleError(
|
||||
"%s: FATAL, no files matched for vars_files import sequence: %s" % (host, sequence)
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
fpath = utils.path_dwim(self.playbook.basedir, utils.template(filename, self.vars))
|
||||
new_vars = utils.parse_yaml_from_file(fpath)
|
||||
self.playbook.SETUP_CACHE[host].update(new_vars)
|
||||
|
@ -0,0 +1,69 @@
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#############################################
|
||||
|
||||
#import ansible.inventory
|
||||
#import ansible.runner
|
||||
#import ansible.constants as C
|
||||
#from ansible import utils
|
||||
#from ansible import errors
|
||||
|
||||
class Task(object):
|
||||
|
||||
__slots__ = [
|
||||
'name', 'action', 'only_if', 'async_seconds', 'async_poll_interval',
|
||||
'notify', 'module_name', 'module_args', 'module_vars', 'play', 'notified_by',
|
||||
]
|
||||
|
||||
def __init__(self, play, ds):
|
||||
|
||||
self.play = play
|
||||
|
||||
# FIXME: error handling on invalid fields
|
||||
# action...
|
||||
|
||||
self.name = ds.get('name', None)
|
||||
self.action = ds.get('action', '')
|
||||
self.notified_by = []
|
||||
|
||||
if self.name is None:
|
||||
self.name = self.action
|
||||
|
||||
self.only_if = ds.get('only_if', 'True')
|
||||
self.async_seconds = int(ds.get('async', 0)) # not async by default
|
||||
self.async_poll_interval = int(ds.get('poll', 10)) # default poll = 10 seconds
|
||||
self.notify = ds.get('notify', [])
|
||||
if isinstance(self.notify, basestring):
|
||||
self.notify = [ self.notify ]
|
||||
|
||||
tokens = self.action.split(None, 1)
|
||||
if len(tokens) < 1:
|
||||
# FIXME: better error handling
|
||||
raise Exception("invalid action in task: %s" % ds)
|
||||
|
||||
self.module_name = tokens[0]
|
||||
self.module_args = ''
|
||||
if len(tokens) > 1:
|
||||
self.module_args = tokens[1]
|
||||
|
||||
# include task specific vars
|
||||
self.module_vars = ds.get('vars', {})
|
||||
if 'first_available_file' in ds:
|
||||
self.module_vars['first_available_file'] = ds.get('first_available_file')
|
||||
|
||||
|
Loading…
Reference in New Issue