Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
ansible
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
ansible
Commits
b9b53d19
Commit
b9b53d19
authored
May 26, 2012
by
Michael DeHaan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Playbook refactoring -- work in progress.
parent
cf9ddf3a
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
307 additions
and
342 deletions
+307
-342
examples/playbooks/intermediate_example.yml
+1
-1
lib/ansible/playbook/__init__.py
+60
-340
lib/ansible/playbook/play.py
+176
-0
lib/ansible/playbook/task.py
+69
-0
lib/ansible/utils.py
+1
-1
No files found.
examples/playbooks/intermediate_example.yml
View file @
b9b53d19
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
# because of the comments it's less concise than it normally is. But feel
# because of the comments it's less concise than it normally is. But feel
# free to comment your playbooks if you like.
# free to comment your playbooks if you like.
-
hosts
:
dbservers
-
hosts
:
all
# we can define variables the normal way...
# we can define variables the normal way...
...
...
lib/ansible/playbook/__init__.py
View file @
b9b53d19
...
@@ -23,9 +23,7 @@ import ansible.constants as C
...
@@ -23,9 +23,7 @@ import ansible.constants as C
from
ansible
import
utils
from
ansible
import
utils
from
ansible
import
errors
from
ansible
import
errors
import
os
import
os
from
play
import
Play
# used to transfer variables to Runner
SETUP_CACHE
=
{
}
#############################################
#############################################
...
@@ -82,6 +80,8 @@ class PlayBook(object):
...
@@ -82,6 +80,8 @@ class PlayBook(object):
sudo: if not specified per play, requests all plays use sudo mode
sudo: if not specified per play, requests all plays use sudo mode
"""
"""
self
.
SETUP_CACHE
=
{}
if
playbook
is
None
or
callbacks
is
None
or
runner_callbacks
is
None
or
stats
is
None
:
if
playbook
is
None
or
callbacks
is
None
or
runner_callbacks
is
None
or
stats
is
None
:
raise
Exception
(
'missing required arguments'
)
raise
Exception
(
'missing required arguments'
)
...
@@ -108,132 +108,11 @@ class PlayBook(object):
...
@@ -108,132 +108,11 @@ class PlayBook(object):
self
.
inventory
=
ansible
.
inventory
.
Inventory
(
host_list
)
self
.
inventory
=
ansible
.
inventory
.
Inventory
(
host_list
)
if
not
self
.
inventory
.
_is_script
:
if
not
self
.
inventory
.
_is_script
:
self
.
global_vars
.
update
(
self
.
inventory
.
get_group_variables
(
'all'
))
self
.
global_vars
.
update
(
self
.
inventory
.
get_group_variables
(
'all'
))
self
.
basedir
=
os
.
path
.
dirname
(
playbook
)
self
.
basedir
=
os
.
path
.
dirname
(
playbook
)
self
.
playbook
=
self
.
_parse_playbook
(
playbook
)
self
.
playbook
=
utils
.
parse_yaml_from_file
(
playbook
)
# *****************************************************
def
_get_vars
(
self
,
play
,
dirname
):
''' load the vars section from a play '''
if
play
.
get
(
'vars'
)
is
None
:
play
[
'vars'
]
=
{}
if
type
(
play
[
'vars'
])
not
in
[
dict
,
list
]:
raise
errors
.
AnsibleError
(
"'vars' section must contain only key/value pairs"
)
vars
=
self
.
global_vars
# translate a list of vars into a dict
if
type
(
play
[
'vars'
])
==
list
:
for
item
in
play
[
'vars'
]:
k
,
v
=
item
.
items
()[
0
]
vars
[
k
]
=
v
else
:
vars
.
update
(
play
[
'vars'
])
vars_prompt
=
play
.
get
(
'vars_prompt'
,
{})
if
type
(
vars_prompt
)
!=
dict
:
raise
errors
.
AnsibleError
(
"'vars_prompt' section must contain only key/value pairs"
)
for
vname
in
vars_prompt
:
# TODO: make this prompt one line and consider double entry
print
vars_prompt
[
vname
]
vars
[
vname
]
=
self
.
callbacks
.
on_vars_prompt
(
vname
)
results
=
self
.
extra_vars
.
copy
()
results
.
update
(
vars
)
return
results
# *****************************************************
def
_include_tasks
(
self
,
play
,
task
,
dirname
,
new_tasks
):
''' load tasks included from external files. '''
# include: some.yml a=2 b=3 c=4
play_vars
=
self
.
_get_vars
(
play
,
dirname
)
include_tokens
=
utils
.
template
(
task
[
'include'
],
play_vars
,
SETUP_CACHE
)
.
split
()
path
=
utils
.
path_dwim
(
dirname
,
include_tokens
[
0
])
include_vars
=
{}
for
i
,
x
in
enumerate
(
include_tokens
):
if
x
.
find
(
"="
)
!=
-
1
:
(
k
,
v
)
=
x
.
split
(
"="
)
include_vars
[
k
]
=
v
inject_vars
=
play_vars
.
copy
()
inject_vars
.
update
(
include_vars
)
included
=
utils
.
parse_yaml_from_file
(
path
)
for
x
in
included
:
if
len
(
include_vars
):
x
[
"vars"
]
=
include_vars
new_tasks
.
append
(
x
)
# *****************************************************
def
_include_handlers
(
self
,
play
,
handler
,
dirname
,
new_handlers
):
''' load handlers from external files '''
inject_vars
=
self
.
_get_vars
(
play
,
dirname
)
path
=
utils
.
template
(
handler
[
'include'
],
inject_vars
,
SETUP_CACHE
)
path
=
utils
.
path_dwim
(
dirname
,
path
)
included
=
utils
.
template_from_file
(
path
,
inject_vars
,
SETUP_CACHE
)
included
=
utils
.
parse_yaml
(
included
)
for
x
in
included
:
new_handlers
.
append
(
x
)
# *****************************************************
def
_parse_playbook
(
self
,
playbook
):
''' load YAML file, including handling for imported files '''
dirname
=
os
.
path
.
dirname
(
playbook
)
playbook
=
utils
.
parse_yaml_from_file
(
playbook
)
for
play
in
playbook
:
tasks
=
play
.
get
(
'tasks'
,[])
handlers
=
play
.
get
(
'handlers'
,
[])
# process tasks in this file as well as imported tasks
new_tasks
=
[]
for
task
in
tasks
:
if
'include'
in
task
:
self
.
_include_tasks
(
play
,
task
,
dirname
,
new_tasks
)
else
:
new_tasks
.
append
(
task
)
# now new_tasks contains a list of tasks, but tasks may contain
# lists of with_items to loop over. Do that.
# TODO: refactor into subfunction
new_tasks2
=
[]
for
task
in
new_tasks
:
if
'with_items'
in
task
:
for
item
in
task
[
'with_items'
]:
produced_task
=
{}
name
=
task
.
get
(
'name'
,
task
.
get
(
'action'
,
'unnamed task'
))
action
=
task
.
get
(
'action'
,
None
)
only_if
=
task
.
get
(
'only_if'
,
None
)
if
action
is
None
:
raise
errors
.
AnsibleError
(
'action is required'
)
produced_task
=
task
.
copy
()
produced_task
[
'action'
]
=
utils
.
template
(
action
,
dict
(
item
=
item
),
SETUP_CACHE
)
produced_task
[
'name'
]
=
utils
.
template
(
name
,
dict
(
item
=
item
),
SETUP_CACHE
)
if
only_if
:
produced_task
[
'only_if'
]
=
utils
.
template
(
only_if
,
dict
(
item
=
item
),
SETUP_CACHE
)
new_tasks2
.
append
(
produced_task
)
else
:
new_tasks2
.
append
(
task
)
play
[
'tasks'
]
=
new_tasks2
# process handlers as well as imported handlers
new_handlers
=
[]
for
handler
in
handlers
:
if
'include'
in
handler
:
self
.
_include_handlers
(
play
,
handler
,
dirname
,
new_handlers
)
else
:
new_handlers
.
append
(
handler
)
play
[
'handlers'
]
=
new_handlers
return
playbook
# *****************************************************
# *****************************************************
...
@@ -242,8 +121,8 @@ class PlayBook(object):
...
@@ -242,8 +121,8 @@ class PlayBook(object):
# loop through all patterns and run them
# loop through all patterns and run them
self
.
callbacks
.
on_start
()
self
.
callbacks
.
on_start
()
for
p
attern
in
self
.
playbook
:
for
p
lay_ds
in
self
.
playbook
:
self
.
_run_play
(
pattern
)
self
.
_run_play
(
Play
(
self
,
play_ds
)
)
# summarize the results
# summarize the results
results
=
{}
results
=
{}
...
@@ -269,190 +148,88 @@ class PlayBook(object):
...
@@ -269,190 +148,88 @@ class PlayBook(object):
# *****************************************************
# *****************************************************
def
_run_module
(
self
,
pattern
,
module
,
args
,
vars
,
remote_user
,
def
_run_task_internal
(
self
,
task
):
async_seconds
,
async_poll_interval
,
only_if
,
sudo
,
sudo_user
,
transport
,
port
):
''' run a particular module step in a playbook '''
''' run a particular module step in a playbook '''
hosts
=
[
h
for
h
in
self
.
inventory
.
list_hosts
()
if
(
h
not
in
self
.
stats
.
failures
)
and
(
h
not
in
self
.
stats
.
dark
)]
hosts
=
[
h
for
h
in
self
.
inventory
.
list_hosts
()
if
(
h
not
in
self
.
stats
.
failures
)
and
(
h
not
in
self
.
stats
.
dark
)]
self
.
inventory
.
restrict_to
(
hosts
)
self
.
inventory
.
restrict_to
(
hosts
)
if
port
is
None
:
port
=
self
.
remote_port
runner
=
ansible
.
runner
.
Runner
(
runner
=
ansible
.
runner
.
Runner
(
pattern
=
pattern
,
inventory
=
self
.
inventory
,
module_name
=
modul
e
,
pattern
=
task
.
play
.
hosts
,
inventory
=
self
.
inventory
,
module_name
=
task
.
module_nam
e
,
module_args
=
args
,
forks
=
self
.
forks
,
module_args
=
task
.
module_
args
,
forks
=
self
.
forks
,
remote_pass
=
self
.
remote_pass
,
module_path
=
self
.
module_path
,
remote_pass
=
self
.
remote_pass
,
module_path
=
self
.
module_path
,
timeout
=
self
.
timeout
,
remote_user
=
remote_user
,
timeout
=
self
.
timeout
,
remote_user
=
task
.
play
.
remote_user
,
remote_port
=
port
,
module_vars
=
vars
,
remote_port
=
task
.
play
.
remote_port
,
module_vars
=
task
.
module_
vars
,
private_key_file
=
self
.
private_key_file
,
private_key_file
=
self
.
private_key_file
,
setup_cache
=
SETUP_CACHE
,
basedir
=
self
.
basedir
,
setup_cache
=
self
.
SETUP_CACHE
,
basedir
=
self
.
basedir
,
conditional
=
only_if
,
callbacks
=
self
.
runner_callbacks
,
conditional
=
task
.
only_if
,
callbacks
=
self
.
runner_callbacks
,
debug
=
self
.
debug
,
sudo
=
sudo
,
sudo_user
=
sudo_user
,
debug
=
self
.
debug
,
sudo
=
task
.
play
.
sudo
,
sudo_user
=
task
.
play
.
sudo_user
,
transport
=
transport
,
sudo_pass
=
self
.
sudo_pass
,
is_playbook
=
True
transport
=
t
ask
.
play
.
t
ransport
,
sudo_pass
=
self
.
sudo_pass
,
is_playbook
=
True
)
)
if
async_seconds
==
0
:
if
task
.
async_seconds
==
0
:
results
=
runner
.
run
()
results
=
runner
.
run
()
else
:
else
:
results
,
poller
=
runner
.
run_async
(
async_seconds
)
results
,
poller
=
runner
.
run_async
(
task
.
async_seconds
)
self
.
stats
.
compute
(
results
)
self
.
stats
.
compute
(
results
)
if
async_poll_interval
>
0
:
if
task
.
async_poll_interval
>
0
:
# if not polling, playbook requested fire and forget
# if not polling, playbook requested fire and forget, so don't poll
# trust the user wanted that and return immediately
results
=
self
.
_async_poll
(
poller
,
task
.
async_seconds
,
task
.
async_poll_interval
)
results
=
self
.
_async_poll
(
poller
,
async_seconds
,
async_poll_interval
)
self
.
inventory
.
lift_restriction
()
self
.
inventory
.
lift_restriction
()
return
results
return
results
# *****************************************************
# *****************************************************
def
_run_task
(
self
,
pattern
=
None
,
task
=
None
,
def
_run_task
(
self
,
play
,
task
,
is_handler
):
remote_user
=
None
,
handlers
=
None
,
conditional
=
False
,
sudo
=
False
,
sudo_user
=
None
,
transport
=
None
,
port
=
None
):
''' run a single task in the playbook and recursively run any subtasks. '''
''' run a single task in the playbook and recursively run any subtasks. '''
# load the module name and parameters from the task entry
self
.
callbacks
.
on_task_start
(
task
.
name
,
is_handler
)
name
=
task
.
get
(
'name'
,
None
)
action
=
task
.
get
(
'action'
,
None
)
if
action
is
None
:
raise
errors
.
AnsibleError
(
"action is required for each item in tasks: offending task is
%
s"
%
name
if
name
else
"unknown"
)
if
name
is
None
:
name
=
action
only_if
=
task
.
get
(
'only_if'
,
'True'
)
async_seconds
=
int
(
task
.
get
(
'async'
,
0
))
# not async by default
async_poll_interval
=
int
(
task
.
get
(
'poll'
,
10
))
# default poll = 10 seconds
tokens
=
action
.
split
(
None
,
1
)
module_name
=
tokens
[
0
]
module_args
=
''
if
len
(
tokens
)
>
1
:
module_args
=
tokens
[
1
]
# include task specific vars
module_vars
=
task
.
get
(
'vars'
,
{})
if
'first_available_file'
in
task
:
module_vars
[
'first_available_file'
]
=
task
.
get
(
'first_available_file'
)
# tasks can be direct (run on all nodes matching
# the pattern) or conditional, where they ran
# as the result of a change handler on a subset
# of all of the hosts
self
.
callbacks
.
on_task_start
(
name
,
conditional
)
# load up an appropriate ansible runner to
# load up an appropriate ansible runner to run the task in parallel
# run the task in parallel
results
=
self
.
_run_task_internal
(
task
)
results
=
self
.
_run_module
(
pattern
,
module_name
,
module_args
,
module_vars
,
remote_user
,
async_seconds
,
async_poll_interval
,
only_if
,
sudo
,
sudo_user
,
transport
,
port
)
# add facts to the global setup cache
# add facts to the global setup cache
for
host
,
result
in
results
[
'contacted'
]
.
iteritems
():
for
host
,
result
in
results
[
'contacted'
]
.
iteritems
():
if
"ansible_facts"
in
result
:
if
"ansible_facts"
in
result
:
for
k
,
v
in
result
[
'ansible_facts'
]
.
iteritems
():
for
k
,
v
in
result
[
'ansible_facts'
]
.
iteritems
():
SETUP_CACHE
[
host
][
k
]
=
v
self
.
SETUP_CACHE
[
host
][
k
]
=
v
self
.
stats
.
compute
(
results
)
self
.
stats
.
compute
(
results
)
# if no hosts are matched, carry on, unlike /bin/ansible
# if no hosts are matched, carry on
# which would warn you about this
if
results
is
None
:
if
results
is
None
:
results
=
{}
results
=
{}
# flag which notify handlers need to be run
# flag which notify handlers need to be run
# this will be on a SUBSET of the actual host list. For instance
if
len
(
task
.
notify
)
>
0
:
# a file might need to be written on only half of the nodes so
# we would only trigger restarting Apache on half of the nodes
subtasks
=
task
.
get
(
'notify'
,
[])
if
isinstance
(
subtasks
,
basestring
):
subtasks
=
[
subtasks
]
if
len
(
subtasks
)
>
0
:
for
host
,
results
in
results
.
get
(
'contacted'
,{})
.
iteritems
():
for
host
,
results
in
results
.
get
(
'contacted'
,{})
.
iteritems
():
if
results
.
get
(
'changed'
,
False
):
if
results
.
get
(
'changed'
,
False
):
for
subtask
in
subtasks
:
for
handler_name
in
task
.
notify
:
self
.
_flag_handler
(
handlers
,
subtask
,
host
)
self
.
_flag_handler
(
play
.
handlers
(),
handler_name
,
host
)
# *****************************************************
# *****************************************************
def
_flag_handler
(
self
,
handlers
,
match
_name
,
host
):
def
_flag_handler
(
self
,
handlers
,
handler
_name
,
host
):
'''
'''
if a task has any notify elements, flag handlers for run
if a task has any notify elements, flag handlers for run
at end of execution cycle for hosts that have indicated
at end of execution cycle for hosts that have indicated
changes have been made
changes have been made
'''
'''
# for all registered handlers in the ansible playbook
# for this particular pattern group
found
=
False
found
=
False
for
x
in
handlers
:
for
x
in
handlers
:
name
=
x
.
get
(
'name'
,
None
)
if
handler_name
==
x
.
name
:
if
name
is
None
:
raise
errors
.
AnsibleError
(
'handler is missing a name'
)
if
match_name
==
name
:
found
=
True
found
=
True
self
.
callbacks
.
on_notify
(
host
,
name
)
self
.
callbacks
.
on_notify
(
host
,
x
.
name
)
# flag the handler with the list of hosts it needs to be run on, it will be run later
x
.
notified_by
.
append
(
host
)
if
not
'run'
in
x
:
x
[
'run'
]
=
[]
x
[
'run'
]
.
append
(
host
)
if
not
found
:
if
not
found
:
raise
errors
.
AnsibleError
(
"change handler (
%
s) is not defined"
%
match
_name
)
raise
errors
.
AnsibleError
(
"change handler (
%
s) is not defined"
%
handler
_name
)
# *****************************************************
# *****************************************************
def
_do_conditional_imports
(
self
,
vars_files
,
pattern
=
None
):
def
_do_setup_step
(
self
,
play
,
vars_files
=
None
):
''' handle the vars_files section, which can contain variables '''
# FIXME: save parsed variable results in memory to avoid excessive re-reading/parsing
# FIXME: currently parses imports for hosts not in the pattern, that is not wrong, but it's
# not super optimized yet either, because we wouldn't have hit them, ergo
# it will raise false errors if there is no defaults variable file without any $vars
# in it, which could happen on uncontacted hosts.
if
type
(
vars_files
)
!=
list
:
raise
errors
.
AnsibleError
(
"vars_files must be a list"
)
host_list
=
[
h
for
h
in
self
.
inventory
.
list_hosts
(
pattern
)
if
not
(
h
in
self
.
stats
.
failures
or
h
in
self
.
stats
.
dark
)
]
for
host
in
host_list
:
cache_vars
=
SETUP_CACHE
.
get
(
host
,{})
SETUP_CACHE
[
host
]
=
cache_vars
for
filename
in
vars_files
:
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
.
path_dwim
(
self
.
basedir
,
utils
.
template
(
real_filename
,
cache_vars
,
SETUP_CACHE
))
sequence
.
append
(
filename2
)
if
os
.
path
.
exists
(
filename2
):
found
=
True
data
=
utils
.
parse_yaml_from_file
(
filename2
)
SETUP_CACHE
[
host
]
.
update
(
data
)
self
.
callbacks
.
on_import_for_host
(
host
,
filename2
)
break
else
:
self
.
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
:
filename2
=
utils
.
path_dwim
(
self
.
basedir
,
utils
.
template
(
filename
,
cache_vars
,
SETUP_CACHE
))
if
not
os
.
path
.
exists
(
filename2
):
raise
errors
.
AnsibleError
(
"no file matched for vars_file import:
%
s"
%
filename2
)
data
=
utils
.
parse_yaml_from_file
(
filename2
)
SETUP_CACHE
[
host
]
.
update
(
data
)
self
.
callbacks
.
on_import_for_host
(
host
,
filename2
)
# *****************************************************
def
_do_setup_step
(
self
,
pattern
,
vars
,
user
,
port
,
sudo
,
sudo_user
,
transport
,
vars_files
=
None
):
''' push variables down to the systems and get variables+facts back up '''
''' push variables down to the systems and get variables+facts back up '''
# this enables conditional includes like $facter_os.yml and is only done
# this enables conditional includes like $facter_os.yml and is only done
...
@@ -461,27 +238,22 @@ class PlayBook(object):
...
@@ -461,27 +238,22 @@ class PlayBook(object):
if
vars_files
is
not
None
:
if
vars_files
is
not
None
:
self
.
callbacks
.
on_setup_secondary
()
self
.
callbacks
.
on_setup_secondary
()
self
.
_do_conditional_imports
(
vars_files
,
pattern
)
play
.
update_vars_files
(
self
.
inventory
.
list_hosts
(
play
.
hosts
)
)
else
:
else
:
self
.
callbacks
.
on_setup_primary
()
self
.
callbacks
.
on_setup_primary
()
host_list
=
[
h
for
h
in
self
.
inventory
.
list_hosts
(
p
attern
)
host_list
=
[
h
for
h
in
self
.
inventory
.
list_hosts
(
p
lay
.
hosts
)
if
not
(
h
in
self
.
stats
.
failures
or
h
in
self
.
stats
.
dark
)
]
if
not
(
h
in
self
.
stats
.
failures
or
h
in
self
.
stats
.
dark
)
]
self
.
inventory
.
restrict_to
(
host_list
)
self
.
inventory
.
restrict_to
(
host_list
)
# push any variables down to the system
# push any variables down to the system
setup_results
=
ansible
.
runner
.
Runner
(
setup_results
=
ansible
.
runner
.
Runner
(
pattern
=
pattern
,
module_name
=
'setup'
,
pattern
=
play
.
hosts
,
module_name
=
'setup'
,
module_args
=
play
.
vars
,
inventory
=
self
.
inventory
,
module_args
=
vars
,
inventory
=
self
.
inventory
,
forks
=
self
.
forks
,
module_path
=
self
.
module_path
,
timeout
=
self
.
timeout
,
remote_user
=
play
.
remote_user
,
forks
=
self
.
forks
,
module_path
=
self
.
module_path
,
remote_pass
=
self
.
remote_pass
,
remote_port
=
play
.
remote_port
,
private_key_file
=
self
.
private_key_file
,
timeout
=
self
.
timeout
,
remote_user
=
user
,
setup_cache
=
self
.
SETUP_CACHE
,
callbacks
=
self
.
runner_callbacks
,
sudo
=
play
.
sudo
,
sudo_user
=
play
.
sudo_user
,
remote_pass
=
self
.
remote_pass
,
remote_port
=
port
,
debug
=
self
.
debug
,
transport
=
play
.
transport
,
sudo_pass
=
self
.
sudo_pass
,
is_playbook
=
True
private_key_file
=
self
.
private_key_file
,
setup_cache
=
SETUP_CACHE
,
callbacks
=
self
.
runner_callbacks
,
sudo
=
sudo
,
sudo_user
=
sudo_user
,
debug
=
self
.
debug
,
transport
=
transport
,
sudo_pass
=
self
.
sudo_pass
,
is_playbook
=
True
)
.
run
()
)
.
run
()
self
.
stats
.
compute
(
setup_results
,
setup
=
True
)
self
.
stats
.
compute
(
setup_results
,
setup
=
True
)
...
@@ -494,83 +266,31 @@ class PlayBook(object):
...
@@ -494,83 +266,31 @@ class PlayBook(object):
# first pass only or we'll erase good work
# first pass only or we'll erase good work
for
(
host
,
result
)
in
setup_ok
.
iteritems
():
for
(
host
,
result
)
in
setup_ok
.
iteritems
():
if
'ansible_facts'
in
result
:
if
'ansible_facts'
in
result
:
SETUP_CACHE
[
host
]
=
result
[
'ansible_facts'
]
self
.
SETUP_CACHE
[
host
]
=
result
[
'ansible_facts'
]
return
setup_results
# *****************************************************
# *****************************************************
def
_run_play
(
self
,
p
g
):
def
_run_play
(
self
,
p
lay
):
''' run a list of tasks for a given pattern, in order '''
''' run a list of tasks for a given pattern, in order '''
# get configuration information about the pattern
self
.
callbacks
.
on_play_start
(
play
.
name
)
pattern
=
pg
.
get
(
'hosts'
)
if
pattern
is
None
:
raise
errors
.
AnsibleError
(
'hosts declaration is required'
)
if
isinstance
(
pattern
,
list
):
pattern
=
';'
.
join
(
pattern
)
pattern
=
utils
.
template
(
pattern
,
self
.
extra_vars
,
{})
name
=
pg
.
get
(
'name'
,
pattern
)
vars
=
self
.
_get_vars
(
pg
,
self
.
basedir
)
vars_files
=
pg
.
get
(
'vars_files'
,
{})
tasks
=
pg
.
get
(
'tasks'
,
[])
handlers
=
pg
.
get
(
'handlers'
,
[])
user
=
pg
.
get
(
'user'
,
self
.
remote_user
)
port
=
pg
.
get
(
'port'
,
self
.
remote_port
)
sudo
=
pg
.
get
(
'sudo'
,
self
.
sudo
)
sudo_user
=
pg
.
get
(
'sudo_user'
,
self
.
sudo_user
)
transport
=
pg
.
get
(
'connection'
,
self
.
transport
)
# the default sudo user is root, so if you change it, sudo is implied
if
sudo_user
!=
'root'
:
sudo
=
True
self
.
callbacks
.
on_play_start
(
name
)
# push any variables down to the system # and get facts/ohai/other data back up
# push any variables down to the system # and get facts/ohai/other data back up
self
.
_do_setup_step
(
pattern
,
vars
,
user
,
port
,
sudo
,
sudo_user
,
transport
,
None
)
rc
=
self
.
_do_setup_step
(
play
)
#
pattern, vars, user, port, sudo, sudo_user, transport, None)
# now with that data, handle contentional variable file imports!
# now with that data, handle contentional variable file imports!
if
len
(
vars_files
)
>
0
:
if
len
(
play
.
vars_files
)
>
0
:
self
.
_do_setup_step
(
pattern
,
vars
,
user
,
port
,
sudo
,
sudo_user
,
transport
,
vars_files
)
rc
=
self
.
_do_setup_step
(
play
,
play
.
vars_files
)
# run all the top level tasks, these get run on every node
# run all the top level tasks, these get run on every node
for
task
in
tasks
:
for
task
in
play
.
tasks
():
self
.
_run_task
(
self
.
_run_task
(
play
,
task
,
False
)
pattern
=
pattern
,
task
=
task
,
# run notify actions
handlers
=
handlers
,
for
handler
in
play
.
handlers
():
remote_user
=
user
,
if
len
(
handler
.
notified_by
)
>
0
:
sudo
=
sudo
,
self
.
inventory
.
restrict_to
(
handler
.
notified_by
)
sudo_user
=
sudo_user
,
self
.
_run_task
(
play
,
handler
,
True
)
transport
=
transport
,
port
=
port
)
# handlers only run on certain nodes, they are flagged by _flag_handlers
# above. They only run on nodes when things mark them as changed, and
# handlers only get run once. For instance, the system is designed
# such that multiple config files if changed can ask for an Apache restart
# but Apache will only be restarted once (at the end).
for
task
in
handlers
:
triggered_by
=
task
.
get
(
'run'
,
None
)
if
type
(
triggered_by
)
==
list
:
self
.
inventory
.
restrict_to
(
triggered_by
)
self
.
_run_task
(
pattern
=
pattern
,
task
=
task
,
handlers
=
[],
conditional
=
True
,
remote_user
=
user
,
sudo
=
sudo
,
sudo_user
=
sudo_user
,
transport
=
transport
,
port
=
port
)
self
.
inventory
.
lift_restriction
()
self
.
inventory
.
lift_restriction
()
# end of execution for this particular pattern. Multiple patterns
# can be in a single playbook file
lib/ansible/playbook/play.py
0 → 100644
View file @
b9b53d19
# (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
)
lib/ansible/playbook/task.py
0 → 100644
View file @
b9b53d19
# (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'
)
lib/ansible/utils.py
View file @
b9b53d19
...
@@ -254,7 +254,7 @@ def varReplace(raw, vars):
...
@@ -254,7 +254,7 @@ def varReplace(raw, vars):
return
''
.
join
(
done
)
return
''
.
join
(
done
)
def
template
(
text
,
vars
,
setup_cache
,
no_engine
=
True
):
def
template
(
text
,
vars
,
setup_cache
=
None
,
no_engine
=
True
):
''' run a text buffer through the templating engine '''
''' run a text buffer through the templating engine '''
vars
=
vars
.
copy
()
vars
=
vars
.
copy
()
vars
[
'hostvars'
]
=
setup_cache
vars
[
'hostvars'
]
=
setup_cache
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment