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
fc4ba46d
Commit
fc4ba46d
authored
Feb 26, 2012
by
Michael DeHaan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a ton of comments so folks can understand what runner does. More refactoring is
certaintly possible too.
parent
c0ac0e9b
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
55 additions
and
31 deletions
+55
-31
lib/ansible/runner.py
+55
-31
No files found.
lib/ansible/runner.py
View file @
fc4ba46d
...
@@ -58,6 +58,7 @@ class Runner(object):
...
@@ -58,6 +58,7 @@ class Runner(object):
remote_pass -- provide only if you don't want to use keys or ssh-agent
remote_pass -- provide only if you don't want to use keys or ssh-agent
'''
'''
# save input values
self
.
host_list
=
self
.
_parse_hosts
(
host_list
)
self
.
host_list
=
self
.
_parse_hosts
(
host_list
)
self
.
module_path
=
module_path
self
.
module_path
=
module_path
self
.
module_name
=
module_name
self
.
module_name
=
module_name
...
@@ -71,18 +72,23 @@ class Runner(object):
...
@@ -71,18 +72,23 @@ class Runner(object):
def
_parse_hosts
(
self
,
host_list
):
def
_parse_hosts
(
self
,
host_list
):
''' parse the host inventory file if not sent as an array '''
''' parse the host inventory file if not sent as an array '''
# if the host list is given as a string load the host list
# from a file, one host per line
if
type
(
host_list
)
!=
list
:
if
type
(
host_list
)
!=
list
:
host_list
=
os
.
path
.
expanduser
(
host_list
)
host_list
=
os
.
path
.
expanduser
(
host_list
)
return
file
(
host_list
)
.
read
()
.
split
(
"
\n
"
)
return
file
(
host_list
)
.
read
()
.
split
(
"
\n
"
)
return
host_list
return
host_list
def
_matches
(
self
,
host_name
,
pattern
=
None
):
def
_matches
(
self
,
host_name
,
pattern
=
None
):
''' returns if a hostname is matched by the pattern '''
''' returns if a hostname is matched by the pattern '''
# a pattern is in fnmatch format but more than one pattern
# can be strung together with semicolons. ex:
# atlanta-web*.example.com;dc-web*.example.com
if
host_name
==
''
:
if
host_name
==
''
:
return
False
return
False
if
not
pattern
:
pattern
=
self
.
pattern
subpatterns
=
pattern
.
split
(
";"
)
subpatterns
=
pattern
.
split
(
";"
)
for
subpattern
in
subpatterns
:
for
subpattern
in
subpatterns
:
if
fnmatch
.
fnmatch
(
host_name
,
subpattern
):
if
fnmatch
.
fnmatch
(
host_name
,
subpattern
):
...
@@ -98,48 +104,66 @@ class Runner(object):
...
@@ -98,48 +104,66 @@ class Runner(object):
ssh
=
paramiko
.
SSHClient
()
ssh
=
paramiko
.
SSHClient
()
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
try
:
try
:
# try paramiko
ssh
.
connect
(
host
,
username
=
self
.
remote_user
,
allow_agent
=
True
,
ssh
.
connect
(
host
,
username
=
self
.
remote_user
,
allow_agent
=
True
,
look_for_keys
=
True
,
password
=
self
.
remote_pass
)
look_for_keys
=
True
,
password
=
self
.
remote_pass
)
return
[
True
,
ssh
]
return
[
True
,
ssh
]
except
:
except
:
# it failed somehow, return the failure string
return
[
False
,
traceback
.
format_exc
()
]
return
[
False
,
traceback
.
format_exc
()
]
def
_return_from_module
(
self
,
conn
,
host
,
result
):
def
_return_from_module
(
self
,
conn
,
host
,
result
):
''' helper function to handle JSON parsing of results '''
# disconnect from paramiko/SSH
conn
.
close
()
conn
.
close
()
try
:
try
:
# try to parse the JSON response
return
[
host
,
True
,
json
.
loads
(
result
)
]
return
[
host
,
True
,
json
.
loads
(
result
)
]
except
:
except
:
# it failed, say so, but return the string anyway
return
[
host
,
False
,
result
]
return
[
host
,
False
,
result
]
def
_delete_remote_files
(
self
,
conn
,
files
):
def
_delete_remote_files
(
self
,
conn
,
files
):
''' deletes one or more remote files '''
for
filename
in
files
:
for
filename
in
files
:
self
.
_exec_command
(
conn
,
"rm -f
%
s"
%
filename
)
self
.
_exec_command
(
conn
,
"rm -f
%
s"
%
filename
)
def
_transfer_file
(
self
,
conn
,
source
,
dest
):
def
_transfer_file
(
self
,
conn
,
source
,
dest
):
''' transfers a remote file '''
self
.
remote_log
(
conn
,
'COPY remote:
%
s local:
%
s'
%
(
source
,
dest
))
self
.
remote_log
(
conn
,
'COPY remote:
%
s local:
%
s'
%
(
source
,
dest
))
ftp
=
conn
.
open_sftp
()
s
ftp
=
conn
.
open_sftp
()
ftp
.
put
(
source
,
dest
)
s
ftp
.
put
(
source
,
dest
)
ftp
.
close
()
s
ftp
.
close
()
def
_transfer_module
(
self
,
conn
):
def
_transfer_module
(
self
,
conn
):
'''
transfers a module file to the remote side to execute it,
but does not execute it yet
'''
outpath
=
self
.
_copy_module
(
conn
)
outpath
=
self
.
_copy_module
(
conn
)
self
.
_exec_command
(
conn
,
"chmod +x
%
s"
%
outpath
)
self
.
_exec_command
(
conn
,
"chmod +x
%
s"
%
outpath
)
return
outpath
return
outpath
def
_execute_module
(
self
,
conn
,
outpath
):
def
_execute_module
(
self
,
conn
,
outpath
):
'''
runs a module that has already been transferred
'''
cmd
=
self
.
_command
(
outpath
)
cmd
=
self
.
_command
(
outpath
)
result
=
self
.
_exec_command
(
conn
,
cmd
)
result
=
self
.
_exec_command
(
conn
,
cmd
)
self
.
_delete_remote_files
(
conn
,
[
outpath
])
self
.
_delete_remote_files
(
conn
,
[
outpath
])
return
result
return
result
def
_execute_normal_module
(
self
,
conn
,
host
):
def
_execute_normal_module
(
self
,
conn
,
host
):
''' transfer a module, set it executable, and run it '''
'''
transfer & execute a module that is not 'copy' or 'template'
because those require extra work.
'''
module
=
self
.
_transfer_module
(
conn
)
module
=
self
.
_transfer_module
(
conn
)
result
=
self
.
_execute_module
(
conn
,
module
)
result
=
self
.
_execute_module
(
conn
,
module
)
return
self
.
_return_from_module
(
conn
,
host
,
result
)
return
self
.
_return_from_module
(
conn
,
host
,
result
)
def
_parse_kv
(
self
,
args
):
def
_parse_kv
(
self
,
args
):
''' helper function to convert a string of key/value items to a dict '''
options
=
{}
options
=
{}
for
x
in
args
:
for
x
in
args
:
if
x
.
find
(
"="
)
!=
-
1
:
if
x
.
find
(
"="
)
!=
-
1
:
...
@@ -150,10 +174,12 @@ class Runner(object):
...
@@ -150,10 +174,12 @@ class Runner(object):
def
_execute_copy
(
self
,
conn
,
host
):
def
_execute_copy
(
self
,
conn
,
host
):
''' handler for file transfer operations '''
''' handler for file transfer operations '''
#
transfer the file to a remote tmp location
#
load up options
options
=
self
.
_parse_kv
(
self
.
module_args
)
options
=
self
.
_parse_kv
(
self
.
module_args
)
source
=
options
[
'src'
]
source
=
options
[
'src'
]
dest
=
options
[
'dest'
]
dest
=
options
[
'dest'
]
# transfer the file to a remote tmp location
tmp_src
=
self
.
_get_tmp_path
(
conn
,
dest
.
split
(
"/"
)[
-
1
])
tmp_src
=
self
.
_get_tmp_path
(
conn
,
dest
.
split
(
"/"
)[
-
1
])
self
.
_transfer_file
(
conn
,
source
,
tmp_src
)
self
.
_transfer_file
(
conn
,
source
,
tmp_src
)
...
@@ -170,6 +196,7 @@ class Runner(object):
...
@@ -170,6 +196,7 @@ class Runner(object):
def
_execute_template
(
self
,
conn
,
host
):
def
_execute_template
(
self
,
conn
,
host
):
''' handler for template operations '''
''' handler for template operations '''
# load up options
options
=
self
.
_parse_kv
(
self
.
module_args
)
options
=
self
.
_parse_kv
(
self
.
module_args
)
source
=
options
[
'src'
]
source
=
options
[
'src'
]
dest
=
options
[
'dest'
]
dest
=
options
[
'dest'
]
...
@@ -199,6 +226,10 @@ class Runner(object):
...
@@ -199,6 +226,10 @@ class Runner(object):
or a traceback string
or a traceback string
'''
'''
# depending on whether it's a normal module,
# or a request to use the copy or template
# module, call the appropriate executor function
ok
,
conn
=
self
.
_connect
(
host
)
ok
,
conn
=
self
.
_connect
(
host
)
if
not
ok
:
if
not
ok
:
return
[
host
,
False
,
conn
]
return
[
host
,
False
,
conn
]
...
@@ -209,19 +240,23 @@ class Runner(object):
...
@@ -209,19 +240,23 @@ class Runner(object):
elif
self
.
module_name
==
'template'
:
elif
self
.
module_name
==
'template'
:
return
self
.
_execute_template
(
conn
,
host
)
return
self
.
_execute_template
(
conn
,
host
)
else
:
else
:
# this would be a coding error in THIS module
# shouldn't occur
raise
Exception
(
"???"
)
raise
Exception
(
"???"
)
def
_command
(
self
,
outpath
):
def
_command
(
self
,
outpath
):
''' form up a command string '''
''' form up a command string
for running over SSH
'''
cmd
=
"
%
s
%
s"
%
(
outpath
,
" "
.
join
(
self
.
module_args
))
cmd
=
"
%
s
%
s"
%
(
outpath
,
" "
.
join
(
self
.
module_args
))
return
cmd
return
cmd
def
remote_log
(
self
,
conn
,
msg
):
def
remote_log
(
self
,
conn
,
msg
):
''' this is the function we use to log things '''
stdin
,
stdout
,
stderr
=
conn
.
exec_command
(
'/usr/bin/logger -t ansible -p auth.info
%
r'
%
msg
)
stdin
,
stdout
,
stderr
=
conn
.
exec_command
(
'/usr/bin/logger -t ansible -p auth.info
%
r'
%
msg
)
# TODO: doesn't actually call logger on the remote box, should though
# TODO: maybe make that optional
def
_exec_command
(
self
,
conn
,
cmd
):
def
_exec_command
(
self
,
conn
,
cmd
):
''' execute a command
over SSH
'''
''' execute a command
string over SSH, return the output
'''
msg
=
'
%
s:
%
s'
%
(
self
.
module_name
,
cmd
)
msg
=
'
%
s:
%
s'
%
(
self
.
module_name
,
cmd
)
self
.
remote_log
(
conn
,
msg
)
self
.
remote_log
(
conn
,
msg
)
stdin
,
stdout
,
stderr
=
conn
.
exec_command
(
cmd
)
stdin
,
stdout
,
stderr
=
conn
.
exec_command
(
cmd
)
...
@@ -229,11 +264,12 @@ class Runner(object):
...
@@ -229,11 +264,12 @@ class Runner(object):
return
results
return
results
def
_get_tmp_path
(
self
,
conn
,
file_name
):
def
_get_tmp_path
(
self
,
conn
,
file_name
):
''' gets a temporary path on a remote box '''
output
=
self
.
_exec_command
(
conn
,
"mktemp /tmp/
%
s.XXXXXX"
%
file_name
)
output
=
self
.
_exec_command
(
conn
,
"mktemp /tmp/
%
s.XXXXXX"
%
file_name
)
return
output
.
split
(
"
\n
"
)[
0
]
return
output
.
split
(
"
\n
"
)[
0
]
def
_copy_module
(
self
,
conn
):
def
_copy_module
(
self
,
conn
):
''' transfer a module over SFTP '''
''' transfer a module over SFTP
, does not run it
'''
in_path
=
os
.
path
.
expanduser
(
in_path
=
os
.
path
.
expanduser
(
os
.
path
.
join
(
self
.
module_path
,
self
.
module_name
)
os
.
path
.
join
(
self
.
module_path
,
self
.
module_name
)
)
)
...
@@ -244,17 +280,18 @@ class Runner(object):
...
@@ -244,17 +280,18 @@ class Runner(object):
sftp
.
close
()
sftp
.
close
()
return
out_path
return
out_path
def
match_hosts
(
self
,
pattern
=
None
):
def
match_hosts
(
self
,
pattern
):
''' return all matched hosts '''
''' return all matched hosts
fitting a pattern
'''
return
[
h
for
h
in
self
.
host_list
if
self
.
_matches
(
h
,
pattern
)
]
return
[
h
for
h
in
self
.
host_list
if
self
.
_matches
(
h
,
pattern
)
]
def
run
(
self
):
def
run
(
self
):
''' xfer & run module on all matched hosts '''
''' xfer & run module on all matched hosts '''
# find hosts that match the pattern
# find hosts that match the pattern
hosts
=
self
.
match_hosts
()
hosts
=
self
.
match_hosts
(
self
.
pattern
)
# attack pool of hosts in N forks
# attack pool of hosts in N forks
# _executor_hook does all of the work
hosts
=
[
(
self
,
x
)
for
x
in
hosts
]
hosts
=
[
(
self
,
x
)
for
x
in
hosts
]
if
self
.
forks
>
1
:
if
self
.
forks
>
1
:
pool
=
multiprocessing
.
Pool
(
self
.
forks
)
pool
=
multiprocessing
.
Pool
(
self
.
forks
)
...
@@ -263,7 +300,9 @@ class Runner(object):
...
@@ -263,7 +300,9 @@ class Runner(object):
results
=
[
_executor_hook
(
x
)
for
x
in
hosts
]
results
=
[
_executor_hook
(
x
)
for
x
in
hosts
]
# sort hosts by ones we successfully contacted
# sort hosts by ones we successfully contacted
# and ones we did not
# and ones we did not so that we can return a
# dictionary containing results of everything
results2
=
{
results2
=
{
"contacted"
:
{},
"contacted"
:
{},
"dark"
:
{}
"dark"
:
{}
...
@@ -278,18 +317,3 @@ class Runner(object):
...
@@ -278,18 +317,3 @@ class Runner(object):
return
results2
return
results2
if
__name__
==
'__main__'
:
# test code...
r
=
Runner
(
host_list
=
DEFAULT_HOST_LIST
,
module_name
=
'ping'
,
module_args
=
''
,
pattern
=
'*'
,
forks
=
3
)
print
r
.
run
()
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