Compare commits

...

34 Commits

Author SHA1 Message Date
5d370bff94 Release bump to v1.2.3 2013-08-21 12:06:26 -05:00
531fd13992 Commenting out default ssh arguments line in ansible.cfg
Also removing the ControlPath option, as it will now be auto-generated
if the ControlPersist option is set in the arguments.

Conflicts:
	examples/ansible.cfg
2013-08-20 21:00:35 -05:00
d5948d59fc Place retry file in the user's home dir instead of /var/lib/tmp
Addresses CVE-2013-4260: predictable filename used for failed results
in world writable directory.
2013-08-20 20:59:25 -05:00
b33ca49289 SSH connection plugin creates ControlPersist socket files in a secure directory
Files were being created in /tmp, but will now be created in $HOME/.ansible/cp/
Addresses CVE-2013-4259: ansible uses a socket with predictable filename in /tmp
2013-08-20 20:58:15 -05:00
ac112b4dab Adding support for hashed known_hosts entries
Fixes Issue #3716 - SSH known host checking needs to understand
hashed known hosts
2013-08-20 20:58:10 -05:00
863b88ba44 Merge pull request #3472 from cristobalrosa/release1.2.2
Fixed monit module. Ignore uppercase letters
2013-07-19 07:35:06 -07:00
1b3d1125d1 Merge pull request #3531 from fractalcat/tmpdir-failure-handling
Handle SSH failures when creating remote tmpdir
2013-07-19 06:14:45 -07:00
690048c797 More informative error message on failure to resolve tmpdir 2013-07-16 09:30:20 +10:00
d21224a7c9 Handle SSH failures when creating remote tmpdir
Bail out if the SSH command fails rather than continuing with an
empty tmp variable.
2013-07-15 17:11:24 +10:00
1f2136b6b3 Remove unnecesary call 2013-07-06 22:13:04 +02:00
90c8dcc4b8 Slight revisement to configuration file comment. 2013-07-05 22:05:05 -04:00
f96ab83a1e Update manpages (for version info) 2013-07-05 22:00:42 -04:00
dfb2dbe26d Update changelog 2013-07-05 21:48:02 -04:00
853e7fb57c Update releases 2013-07-05 21:46:22 -04:00
6241236d3c Update changelogs 2013-07-05 21:45:41 -04:00
30ca72bfd2 Release bump 2013-07-05 21:43:57 -04:00
d4a595b207 Make it possible to tell paramiko to not record new host keys, which can be slow with a large number of hosts.
-c ssh is preferred in most cases if you have ControlPersist available, otherwise if you are comfortable you
can turn off recording while leaving host key checking on, etc.
2013-07-05 21:42:41 -04:00
715f25efbc Fix the comment. 2013-07-05 10:08:18 -04:00
d5cf125757 Fixed monit module. Ignore uppercase letters 2013-07-05 05:58:49 +02:00
f29c98c29f Update the releases list. 2013-07-04 21:50:16 -04:00
bc03a294ae Update release versions for 1.2.1 2013-07-04 21:47:47 -04:00
2e4e064a88 Update packaging changelogs. 2013-07-04 21:47:03 -04:00
032c8408af Update the changelog 2013-07-04 21:44:25 -04:00
c593454fd0 Improve interlaced output prevention when asking for host key approval. 2013-07-04 21:34:11 -04:00
d1eaf2db70 Default to 'smart' transport, which will use OpenSSH if it can support ControlPersist. 2013-07-04 21:33:58 -04:00
8cc2f1c4a9 Lock around SSH connectivity to new hosts in host checking mode such that prompts for host approval
messages do not get interlaced.
2013-07-04 21:31:14 -04:00
9f9373ccb5 Close the named temporary file. 2013-07-04 21:31:05 -04:00
3dd93ee2e4 Closing stdin too early. 2013-07-04 21:30:49 -04:00
f782c237c4 Enable paramiko to ask whether to add keys to known hosts. 2013-07-04 21:30:38 -04:00
2b58264096 Example config file should use correct config setting. 2013-07-04 21:30:27 -04:00
266e689403 Merge 2013-07-04 21:30:20 -04:00
13f93de78a Makes host key checking the default behavior but can be disabled in ansible.cfg or by environment variable. 2013-07-04 21:30:00 -04:00
8584a945d6 Merge pull request #3307 from mplonka/release1.2
Skipping facts not available in AIX WPARs
2013-06-27 19:24:11 -07:00
2b07873eb1 Skipping facts not available in AIX WPARs 2013-06-21 13:32:57 +02:00
21 changed files with 503 additions and 173 deletions

View File

@ -1,6 +1,23 @@
Ansible Changes By Release
==========================
1.2.1 "Hear About It Later" (reprise) -- July 4, 2013
* Added a configuration file option [paramiko_connection] record_host_keys which allows the code that paramiko uses
to update known_hosts to be disabled. This is done because paramiko can be very slow at doing this if you have a
large number of hosts and some folks may not want this behavior. This can be toggled independently of host key checking
and does not affect the ssh transport plugin. Use of the ssh transport plugin is preferred if you have ControlPersist
capability, and Ansible by default in 1.2.1 and later will autodetect.
1.2.1 "Hear About It Later" -- July 4, 2013
* Connection default is now "smart", which discovers if the system openssh can support ControlPersist, and uses
it if so, if not falls back to paramiko.
* Host key checking is on by default. Disable it if you like by adding host_key_checking=False in the [default]
section of /etc/ansible/ansible.cfg or ~/ansible.cfg or by exporting ANSIBLE_HOST_KEY_CHECKING=False
* Paramiko now records host keys it was in contact with host key checking is on. It is somewhat sluggish when doing this,
so switch to the 'ssh' transport if this concerns you.
1.2 "Right Now" -- June 10, 2013
Core Features:

View File

@ -1,17 +1,20 @@
Ansible Releases at a Glance
============================
1.2 "Right Now" ------- pending
1.1 "Mean Street" ----- 04-02-2013
1.0 "Eruption" -------- 02-01-2013
0.9 "Dreams" ---------- 11-30-2012
0.8 "Cathedral" ------- 10-19-2012
0.7 "Panama" ---------- 09-06-2012
0.6 "Cabo" ------------ 08-06-2012
0.5 "Amsterdam" ------- 07-04-2012
0.4 "Unchained" ------- 05-23-2012
0.3 "Baluchitherium" -- 04-23-2012
0.0.2 Untitled
0.0.1 Untitled
1.2.3 "Hear About It Later" -- 08-21-2013
1.2.2 "Hear About It Later" -- 07-05-2013
1.2.1 "Hear About It Later" -- 07-04-2013
1.2 "Right Now" ------------ 06-10-2013
1.1 "Mean Street" ---------- 04-02-2013
1.0 "Eruption" ------------- 02-01-2013
0.9 "Dreams" --------------- 11-30-2012
0.8 "Cathedral" ------------ 10-19-2012
0.7 "Panama" --------------- 09-06-2012
0.6 "Cabo" ----------------- 08-06-2012
0.5 "Amsterdam" ------------ 07-04-2012
0.4 "Unchained" ------------ 05-23-2012
0.3 "Baluchitherium" ------- 04-23-2012
0.0.2 Untitled
0.0.1 Untitled

View File

@ -1 +1,2 @@
1.2
1.2.3

View File

@ -1,13 +1,22 @@
'\" t
.\" Title: ansible-doc
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 04/02/2013
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 08/21/2013
.\" Manual: System administration commands
.\" Source: Ansible 1.2
.\" Source: Ansible 1.2.3
.\" Language: English
.\"
.TH "ANSIBLE\-DOC" "1" "04/02/2013" "Ansible 1\&.2" "System administration commands"
.TH "ANSIBLE\-DOC" "1" "08/21/2013" "Ansible 1\&.2\&.3" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------

View File

@ -1,13 +1,22 @@
'\" t
.\" Title: ansible-playbook
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 06/03/2013
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 08/21/2013
.\" Manual: System administration commands
.\" Source: Ansible 1.2
.\" Source: Ansible 1.2.3
.\" Language: English
.\"
.TH "ANSIBLE\-PLAYBOOK" "1" "06/03/2013" "Ansible 1\&.2" "System administration commands"
.TH "ANSIBLE\-PLAYBOOK" "1" "08/21/2013" "Ansible 1\&.2\&.3" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------

View File

@ -1,13 +1,22 @@
'\" t
.\" Title: ansible
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 04/02/2013
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 08/21/2013
.\" Manual: System administration commands
.\" Source: Ansible 1.2
.\" Source: Ansible 1.2.3
.\" Language: English
.\"
.TH "ANSIBLE" "1" "04/02/2013" "Ansible 1\&.2" "System administration commands"
.TH "ANSIBLE" "1" "08/21/2013" "Ansible 1\&.2\&.3" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@ -22,16 +31,23 @@
ansible-pull \- set up a remote copy of ansible on each managed node
.SH "SYNOPSIS"
.sp
ansible \-d DEST \-U URL [ \-C CHECKOUT ]
ansible \-d DEST \-U URL [ \-C CHECKOUT ] [ \-i INVENTORY ] [ <filename\&.yml> ]
.SH "DESCRIPTION"
.sp
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \'remote things\' over SSH\&.
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \*(Aqremote things\*(Aq over SSH\&.
.sp
Use ansible\-pull to set up a remote copy of ansible on each managed node, each set to run via cron and update playbook source via git\&. This inverts the default \fBpush\fR architecture of ansible into a \fBpull\fR architecture, which has near\-limitless scaling potential\&.
.sp
The setup playbook can be tuned to change the cron frequency, logging locations, and parameters to ansible\-pull\&.
.sp
This is useful both for extreme scale\-out as well as periodic remediation\&. Usage of the \fIfetch\fR module to retrieve logs from ansible\-pull runs would be an excellent way to gather and analyze remote logs from ansible\-pull\&.
.SH "OPTIONAL ARGUMENT"
.PP
\fBfilename\&.yml\fR
.RS 4
The name of one the YAML format files to run as an ansible playbook\&. This can be a relative path within the git checkout\&. If not provided, ansible\-pull will look for a playbook based on the host\(cqs fully\-qualified domain name and finally a playbook named
\fBlocal\&.yml\fR\&.
.RE
.SH "OPTIONS"
.PP
\fB\-d\fR \fIDEST\fR, \fB\-\-directory=\fR\fIDEST\fR
@ -49,6 +65,18 @@ URL of git repository to clone\&.
Branch/Tag/Commit to checkout\&. Defaults to
\fIHEAD\fR\&.
.RE
.PP
\fB\-i\fR \fIPATH\fR, \fB\-\-inventory=\fR\fIPATH\fR
.RS 4
The
\fIPATH\fR
to the inventory hosts file\&. This can be a relative path within the git checkout\&.
.RE
.PP
\fB\-\-purge\fR
.RS 4
Purge the git checkout after the playbook is run\&.
.RE
.SH "AUTHOR"
.sp
Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&.

View File

@ -1,13 +1,22 @@
'\" t
.\" Title: ansible
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 05/29/2013
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 08/21/2013
.\" Manual: System administration commands
.\" Source: Ansible 1.2
.\" Source: Ansible 1.2.3
.\" Language: English
.\"
.TH "ANSIBLE" "1" "05/29/2013" "Ansible 1\&.2" "System administration commands"
.TH "ANSIBLE" "1" "08/21/2013" "Ansible 1\&.2\&.3" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@ -25,7 +34,7 @@ ansible \- run a command somewhere else
ansible <host\-pattern> [\-f forks] [\-m module_name] [\-a args]
.SH "DESCRIPTION"
.sp
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \'remote things\' over SSH\&.
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \*(Aqremote things\*(Aq over SSH\&.
.SH "ARGUMENTS"
.PP
\fBhost\-pattern\fR
@ -73,7 +82,7 @@ search path to load modules from\&. The default is
\fI/usr/share/ansible\fR\&. This can also be set with the ANSIBLE_LIBRARY environment variable\&.
.RE
.PP
\fB\-a\fR \'\fIARGUMENTS\fR\', \fB\-\-args=\fR\'\fIARGUMENTS\fR\'
\fB\-a\fR \*(Aq\fIARGUMENTS\fR\*(Aq, \fB\-\-args=\fR\*(Aq\fIARGUMENTS\fR\*(Aq
.RS 4
The
\fIARGUMENTS\fR

View File

@ -9,74 +9,24 @@
hostfile = /etc/ansible/hosts
# location of ansible library, eliminates need to specify --module-path
library = /usr/share/ansible
hostfile = /etc/ansible/hosts
library = /usr/share/ansible
remote_tmp = $HOME/.ansible/tmp
pattern = *
forks = 5
poll_interval = 15
sudo_user = root
#ask_sudo_pass = True
#ask_pass = True
transport = smart
remote_port = 22
library = /usr/share/ansible
# default module name used in /usr/bin/ansible when -m is not specified
# uncomment this to disable SSH key host checking
#host_key_checking = False
module_name = command
# location for ansible log file. If set, will store output from ansible
# and ansible-playbook. If enabling, you may wish to configure
# logrotate.
#log_path = /var/log/ansible.log
# home directory where temp files are stored on remote systems. Should
# almost always contain $HOME or be a directory writeable by all users
remote_tmp = $HOME/.ansible/tmp
# the default pattern for ansible-playbooks ("hosts:")
pattern = *
# the default number of forks (parallelism) to be used. Usually you
# can crank this up.
forks=5
# the timeout used by various connection types. Usually this corresponds
# to an SSH timeout
timeout=10
# when using --poll or "poll:" in an ansible playbook, and not specifying
# an explicit poll interval, use this interval
poll_interval=15
# when specifying --sudo to /usr/bin/ansible or "sudo:" in a playbook,
# and not specifying "--sudo-user" or "sudo_user" respectively, sudo
# to this user account
sudo_user=root
# the following forces ansible to always ask for the sudo password (instead of having
# to add -K to the commandline). Or you can use the environment variable (ANSIBLE_ASK_SUDO_PASS)
#ask_sudo_pass=True
# the following forces ansible to always ask for the ssh-password (-k)
# can also be set by the environment variable ANSIBLE_ASK_PASS
#ask_pass=True
# connection to use when -c <connection_type> is not specified
transport=paramiko
# remote SSH port to be used when --port or "port:" or an equivalent inventory
# variable is not specified.
remote_port=22
# if set, always run /usr/bin/ansible commands as this user, and assume this value
# if "user:" is not set in a playbook. If not set, use the current Unix user
# as the default
#remote_user=root
# the default sudo executable. If a sudo alternative with a sudo-compatible interface
# is used, specify its executable name as the default
@ -144,17 +94,18 @@ filter_plugins = /usr/share/ansible_plugins/filter_plugins
[paramiko_connection]
# nothing to configure yet
# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered. Increases performance on new host additions. Setting works independently of the
# host key checking setting above.
#record_host_keys=False
[ssh_connection]
# if uncommented, sets the ansible ssh arguments to the following. Leaving off ControlPersist
# will result in poor performance, so use transport=paramiko on older platforms rather than
# removing it
ssh_args=-o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r
# the following makes ansible use scp if the connection type is ssh (default is sftp)
# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it
#ssh_args = -o ControlMaster=auto -o ControlPersist=60s
#scp_if_ssh=True

View File

@ -14,5 +14,5 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
__version__ = '1.2'
__version__ = '1.2.3'
__author__ = 'Michael DeHaan'

View File

@ -79,11 +79,23 @@ def log_lockfile():
LOG_LOCK = open(log_lockfile(), 'w')
def log_flock():
fcntl.flock(LOG_LOCK, fcntl.LOCK_EX)
def log_flock(runner):
fcntl.lockf(LOG_LOCK, fcntl.LOCK_EX)
if runner is not None:
try:
fcntl.lockf(runner.output_lockfile, fcntl.LOCK_EX)
except OSError, e:
# already got closed?
pass
def log_unflock():
fcntl.flock(LOG_LOCK, fcntl.LOCK_UN)
def log_unflock(runner):
fcntl.lockf(LOG_LOCK, fcntl.LOCK_UN)
if runner is not None:
try:
fcntl.lockf(runner.output_lockfile, fcntl.LOCK_UN)
except OSError, e:
# already got closed?
pass
def set_play(callback, play):
''' used to notify callback plugins of context '''
@ -97,9 +109,9 @@ def set_task(callback, task):
for callback_plugin in callback_plugins:
callback_plugin.task = task
def display(msg, color=None, stderr=False, screen_only=False, log_only=False):
def display(msg, color=None, stderr=False, screen_only=False, log_only=False, runner=None):
# prevent a very rare case of interlaced multiprocess I/O
log_flock()
log_flock(runner)
msg2 = msg
if color:
msg2 = stringc(msg, color)
@ -116,7 +128,7 @@ def display(msg, color=None, stderr=False, screen_only=False, log_only=False):
logger.error(msg)
else:
logger.info(msg)
log_unflock()
log_unflock(runner)
def call_callback_module(method_name, *args, **kwargs):
@ -342,7 +354,7 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
def on_unreachable(self, host, res):
if type(res) == dict:
res = res.get('msg','')
display("%s | FAILED => %s" % (host, res), stderr=True, color='red')
display("%s | FAILED => %s" % (host, res), stderr=True, color='red', runner=self.runner)
if self.options.tree:
utils.write_tree_file(
self.options.tree, host,
@ -351,15 +363,15 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
super(CliRunnerCallbacks, self).on_unreachable(host, res)
def on_skipped(self, host, item=None):
display("%s | skipped" % (host))
display("%s | skipped" % (host), runner=self.runner)
super(CliRunnerCallbacks, self).on_skipped(host, item)
def on_error(self, host, err):
display("err: [%s] => %s\n" % (host, err), stderr=True)
display("err: [%s] => %s\n" % (host, err), stderr=True, runner=self.runner)
super(CliRunnerCallbacks, self).on_error(host, err)
def on_no_hosts(self):
display("no hosts matched\n", stderr=True)
display("no hosts matched\n", stderr=True, runner=self.runner)
super(CliRunnerCallbacks, self).on_no_hosts()
def on_async_poll(self, host, res, jid, clock):
@ -367,27 +379,27 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
self._async_notified[jid] = clock + 1
if self._async_notified[jid] > clock:
self._async_notified[jid] = clock
display("<job %s> polling, %ss remaining" % (jid, clock))
display("<job %s> polling, %ss remaining" % (jid, clock), runner=self.runner)
super(CliRunnerCallbacks, self).on_async_poll(host, res, jid, clock)
def on_async_ok(self, host, res, jid):
display("<job %s> finished on %s => %s"%(jid, host, utils.jsonify(res,format=True)))
display("<job %s> finished on %s => %s"%(jid, host, utils.jsonify(res,format=True)), runner=self.runner)
super(CliRunnerCallbacks, self).on_async_ok(host, res, jid)
def on_async_failed(self, host, res, jid):
display("<job %s> FAILED on %s => %s"%(jid, host, utils.jsonify(res,format=True)), color='red', stderr=True)
display("<job %s> FAILED on %s => %s"%(jid, host, utils.jsonify(res,format=True)), color='red', stderr=True, runner=self.runner)
super(CliRunnerCallbacks, self).on_async_failed(host,res,jid)
def _on_any(self, host, result):
result2 = result.copy()
result2.pop('invocation', None)
(msg, color) = host_report_msg(host, self.options.module_name, result2, self.options.one_line)
display(msg, color=color)
display(msg, color=color, runner=self.runner)
if self.options.tree:
utils.write_tree_file(self.options.tree, host, utils.jsonify(result2,format=True))
def on_file_diff(self, host, diff):
display(utils.get_diff(diff))
display(utils.get_diff(diff), runner=self.runner)
super(CliRunnerCallbacks, self).on_file_diff(host, diff)
########################################################################
@ -408,11 +420,12 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
msg = "fatal: [%s] => (item=%s) => %s" % (host, item, results)
else:
msg = "fatal: [%s] => %s" % (host, results)
display(msg, color='red')
display(msg, color='red', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_unreachable(host, results)
def on_failed(self, host, results, ignore_errors=False):
results2 = results.copy()
results2.pop('invocation', None)
@ -429,21 +442,22 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
msg = "failed: [%s] => (item=%s) => %s" % (host, item, utils.jsonify(results2))
else:
msg = "failed: [%s] => %s" % (host, utils.jsonify(results2))
display(msg, color='red')
display(msg, color='red', runner=self.runner)
if stderr:
display("stderr: %s" % stderr, color='red')
display("stderr: %s" % stderr, color='red', runner=self.runner)
if stdout:
display("stdout: %s" % stdout, color='red')
display("stdout: %s" % stdout, color='red', runner=self.runner)
if returned_msg:
display("msg: %s" % returned_msg, color='red')
display("msg: %s" % returned_msg, color='red', runner=self.runner)
if not parsed and module_msg:
display("invalid output was: %s" % module_msg, color='red')
display("invalid output was: %s" % module_msg, color='red', runner=self.runner)
if ignore_errors:
display("...ignoring", color='cyan')
display("...ignoring", color='cyan', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_failed(host, results, ignore_errors=ignore_errors)
def on_ok(self, host, host_result):
item = host_result.get('item', None)
host_result2 = host_result.copy()
@ -473,9 +487,9 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
if msg != '':
if not changed:
display(msg, color='green')
display(msg, color='green', runner=self.runner)
else:
display(msg, color='yellow')
display(msg, color='yellow', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_ok(host, host_result)
def on_error(self, host, err):
@ -487,7 +501,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
else:
msg = "err: [%s] => %s" % (host, err)
display(msg, color='red', stderr=True)
display(msg, color='red', stderr=True, runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_error(host, err)
def on_skipped(self, host, item=None):
@ -496,11 +510,11 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
msg = "skipping: [%s] => (item=%s)" % (host, item)
else:
msg = "skipping: [%s]" % host
display(msg, color='cyan')
display(msg, color='cyan', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_skipped(host, item)
def on_no_hosts(self):
display("FATAL: no hosts matched or all hosts have already failed -- aborting\n", color='red')
display("FATAL: no hosts matched or all hosts have already failed -- aborting\n", color='red', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_no_hosts()
def on_async_poll(self, host, res, jid, clock):
@ -509,21 +523,21 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
if self._async_notified[jid] > clock:
self._async_notified[jid] = clock
msg = "<job %s> polling, %ss remaining"%(jid, clock)
display(msg, color='cyan')
display(msg, color='cyan', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_async_poll(host,res,jid,clock)
def on_async_ok(self, host, res, jid):
msg = "<job %s> finished on %s"%(jid, host)
display(msg, color='cyan')
display(msg, color='cyan', runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_async_ok(host, res, jid)
def on_async_failed(self, host, res, jid):
msg = "<job %s> FAILED on %s" % (jid, host)
display(msg, color='red', stderr=True)
display(msg, color='red', stderr=True, runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_async_failed(host,res,jid)
def on_file_diff(self, host, diff):
display(utils.get_diff(diff))
display(utils.get_diff(diff), runner=self.runner)
super(PlaybookRunnerCallbacks, self).on_file_diff(host, diff)
########################################################################

View File

@ -20,7 +20,25 @@ import pwd
import sys
import ConfigParser
def get_config(p, section, key, env_var, default):
# copied from utils, avoid circular reference fun :)
def mk_boolean(value):
val = str(value)
if val.lower() in [ "true", "t", "y", "1", "yes" ]:
return True
else:
return False
def get_config(p, section, key, env_var, default, boolean=False, integer=False):
''' return a configuration variable with casting '''
value = _get_config(p, section, key, env_var, default)
if boolean:
return mk_boolean(value)
if integer:
return int(value)
return value
def _get_config(p, section, key, env_var, default, boolean=True):
''' helper function for get_config '''
if env_var is not None:
value = os.environ.get(env_var, None)
if value is not None:
@ -81,20 +99,20 @@ DEFAULT_MODULE_LANG = get_config(p, DEFAULTS, 'module_lang', 'ANSIBLE
DEFAULT_TIMEOUT = get_config(p, DEFAULTS, 'timeout', 'ANSIBLE_TIMEOUT', 10)
DEFAULT_POLL_INTERVAL = get_config(p, DEFAULTS, 'poll_interval', 'ANSIBLE_POLL_INTERVAL', 15)
DEFAULT_REMOTE_USER = get_config(p, DEFAULTS, 'remote_user', 'ANSIBLE_REMOTE_USER', active_user)
DEFAULT_ASK_PASS = get_config(p, DEFAULTS, 'ask_pass', 'ANSIBLE_ASK_PASS', False)
DEFAULT_ASK_PASS = get_config(p, DEFAULTS, 'ask_pass', 'ANSIBLE_ASK_PASS', False, boolean=True)
DEFAULT_PRIVATE_KEY_FILE = shell_expand_path(get_config(p, DEFAULTS, 'private_key_file', 'ANSIBLE_PRIVATE_KEY_FILE', None))
DEFAULT_SUDO_USER = get_config(p, DEFAULTS, 'sudo_user', 'ANSIBLE_SUDO_USER', 'root')
DEFAULT_ASK_SUDO_PASS = get_config(p, DEFAULTS, 'ask_sudo_pass', 'ANSIBLE_ASK_SUDO_PASS', False)
DEFAULT_ASK_SUDO_PASS = get_config(p, DEFAULTS, 'ask_sudo_pass', 'ANSIBLE_ASK_SUDO_PASS', False, boolean=True)
DEFAULT_REMOTE_PORT = int(get_config(p, DEFAULTS, 'remote_port', 'ANSIBLE_REMOTE_PORT', 22))
DEFAULT_TRANSPORT = get_config(p, DEFAULTS, 'transport', 'ANSIBLE_TRANSPORT', 'paramiko')
DEFAULT_SCP_IF_SSH = get_config(p, 'ssh_connection', 'scp_if_ssh', 'ANSIBLE_SCP_IF_SSH', False)
DEFAULT_TRANSPORT = get_config(p, DEFAULTS, 'transport', 'ANSIBLE_TRANSPORT', 'smart')
DEFAULT_SCP_IF_SSH = get_config(p, 'ssh_connection', 'scp_if_ssh', 'ANSIBLE_SCP_IF_SSH', False, boolean=True)
DEFAULT_MANAGED_STR = get_config(p, DEFAULTS, 'ansible_managed', None, 'Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}')
DEFAULT_SYSLOG_FACILITY = get_config(p, DEFAULTS, 'syslog_facility', 'ANSIBLE_SYSLOG_FACILITY', 'LOG_USER')
DEFAULT_KEEP_REMOTE_FILES = get_config(p, DEFAULTS, 'keep_remote_files', 'ANSIBLE_KEEP_REMOTE_FILES', '0')
DEFAULT_SUDO_EXE = get_config(p, DEFAULTS, 'sudo_exe', 'ANSIBLE_SUDO_EXE', 'sudo')
DEFAULT_SUDO_FLAGS = get_config(p, DEFAULTS, 'sudo_flags', 'ANSIBLE_SUDO_FLAGS', '-H')
DEFAULT_HASH_BEHAVIOUR = get_config(p, DEFAULTS, 'hash_behaviour', 'ANSIBLE_HASH_BEHAVIOUR', 'replace')
DEFAULT_LEGACY_PLAYBOOK_VARIABLES = get_config(p, DEFAULTS, 'legacy_playbook_variables', 'ANSIBLE_LEGACY_PLAYBOOK_VARIABLES', 'yes')
DEFAULT_LEGACY_PLAYBOOK_VARIABLES = get_config(p, DEFAULTS, 'legacy_playbook_variables', 'ANSIBLE_LEGACY_PLAYBOOK_VARIABLES', True, boolean=True)
DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBLE_JINJA2_EXTENSIONS', None)
DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh')
@ -108,7 +126,10 @@ DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_
ANSIBLE_NOCOWS = get_config(p, DEFAULTS, 'nocows', 'ANSIBLE_NOCOWS', None)
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)
ZEROMQ_PORT = int(get_config(p, 'fireball', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099))
PARAMIKO_RECORD_HOST_KEYS = get_config(p, 'paramiko_connection', 'record_host_keys', 'ANSIBLE_PARAMIKO_RECORD_HOST_KEYS', True, boolean=True)
ZEROMQ_PORT = int(get_config(p, 'fireball_connection', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099))
HOST_KEY_CHECKING = get_config(p, DEFAULTS, 'host_key_checking', 'ANSIBLE_HOST_KEY_CHECKING', True, boolean=True)
# non-configurable things
DEFAULT_SUDO_PASS = None

View File

@ -447,13 +447,7 @@ class PlayBook(object):
basedir = self.inventory.basedir()
filename = "%s.retry" % os.path.basename(self.filename)
filename = filename.replace(".yml","")
if not os.path.exists('/var/tmp/ansible'):
try:
os.makedirs('/var/tmp/ansible')
except:
pass
filename = os.path.join('/var/tmp/ansible', filename)
filename = os.path.join(os.path.expandvars('$HOME/'), filename)
try:
fd = open(filename, 'w')

View File

@ -30,6 +30,7 @@ import base64
import sys
import shlex
import pipes
import subprocess
import ansible.constants as C
import ansible.inventory
@ -49,10 +50,13 @@ except ImportError:
HAS_ATFORK=False
multiprocessing_runner = None
OUTPUT_LOCKFILE = tempfile.TemporaryFile()
PROCESS_LOCKFILE = tempfile.TemporaryFile()
################################################
def _executor_hook(job_queue, result_queue):
def _executor_hook(job_queue, result_queue, new_stdin):
# attempt workaround of https://github.com/newsapps/beeswithmachineguns/issues/17
# this function also not present in CentOS 6
@ -63,7 +67,7 @@ def _executor_hook(job_queue, result_queue):
while not job_queue.empty():
try:
host = job_queue.get(block=False)
return_data = multiprocessing_runner._executor(host)
return_data = multiprocessing_runner._executor(host, new_stdin)
result_queue.put(return_data)
if 'LEGACY_TEMPLATE_WARNING' in return_data.flags:
@ -131,6 +135,10 @@ class Runner(object):
complex_args=None # structured data in addition to module_args, must be a dict
):
# used to lock multiprocess inputs and outputs at various levels
self.output_lockfile = OUTPUT_LOCKFILE
self.process_lockfile = PROCESS_LOCKFILE
if not complex_args:
complex_args = {}
@ -163,9 +171,19 @@ class Runner(object):
self.is_playbook = is_playbook
self.environment = environment
self.complex_args = complex_args
self.callbacks.runner = self
# if the transport is 'smart' see if SSH can support ControlPersist if not use paramiko
# 'smart' is the default since 1.2.1/1.3
if self.transport == 'smart':
cmd = subprocess.Popen(['ssh','-o','ControlPersist'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = cmd.communicate()
if "Bad configuration option" in err:
self.transport = "paramiko"
else:
self.transport = "ssh"
# misc housekeeping
if subset and self.inventory._subset is None:
# don't override subset when passed from playbook
@ -328,7 +346,7 @@ class Runner(object):
# *****************************************************
def _executor(self, host):
def _executor(self, host, new_stdin):
''' handler for multiprocessing library '''
def get_flags():
@ -341,7 +359,9 @@ class Runner(object):
return flags
try:
exec_rc = self._executor_internal(host)
self._new_stdin = new_stdin
exec_rc = self._executor_internal(host, new_stdin)
if type(exec_rc) != ReturnData:
raise Exception("unexpected return type: %s" % type(exec_rc))
exec_rc.flags = get_flags()
@ -360,7 +380,7 @@ class Runner(object):
# *****************************************************
def _executor_internal(self, host):
def _executor_internal(self, host, new_stdin):
''' executes any module one or more times '''
host_variables = self.inventory.get_variables(host)
@ -680,7 +700,16 @@ class Runner(object):
cmd += ' && echo %s' % basetmp
result = self._low_level_exec_command(conn, cmd, None, sudoable=False)
if result['rc'] != 0:
raise errors.AnsibleError('could not create temporary directory: '
'SSH exited with return code %d' % result['rc'])
rc = utils.last_non_blank_line(result['stdout']).strip() + '/'
# Catch any other failure conditions here; files should never be
# written directly to /.
if rc == '/':
raise errors.AnsibleError('failed to resolve remote temporary '
'directory from %s: `%s` returned '
'empty string' % (basetmp, cmd))
return rc
@ -759,8 +788,9 @@ class Runner(object):
workers = []
for i in range(self.forks):
new_stdin = os.fdopen(os.dup(sys.stdin.fileno()))
prc = multiprocessing.Process(target=_executor_hook,
args=(job_queue, result_queue))
args=(job_queue, result_queue, new_stdin))
prc.start()
workers.append(prc)
@ -771,7 +801,7 @@ class Runner(object):
for worker in workers:
worker.terminate()
worker.join()
results = []
try:
while not result_queue.empty():
@ -832,7 +862,7 @@ class Runner(object):
# We aren't iterating over all the hosts in this
# group. So, just pick the first host in our group to
# construct the conn object with.
result_data = self._executor(hosts[0]).result
result_data = self._executor(hosts[0], None).result
# Create a ResultData item for each host in this group
# using the returned result. If we didn't do this we would
# get false reports of dark hosts.
@ -850,7 +880,8 @@ class Runner(object):
raise errors.AnsibleError("interrupted")
raise
else:
results = [ self._executor(h) for h in hosts ]
results = [ self._executor(h, None) for h in hosts ]
return self._partition_results(results)
# *****************************************************

View File

@ -15,15 +15,35 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# ---
# The paramiko transport is provided because many distributions, in particular EL6 and before
# do not support ControlPersist in their SSH implementations. This is needed on the Ansible
# control machine to be reasonably efficient with connections. Thus paramiko is faster
# for most users on these platforms. Users with ControlPersist capability can consider
# using -c ssh or configuring the transport in ansible.cfg.
import warnings
import os
import pipes
import socket
import random
import logging
import traceback
import fcntl
import sys
from termios import tcflush, TCIFLUSH
from binascii import hexlify
from ansible.callbacks import vvv
from ansible import errors
from ansible import utils
from ansible import constants as C
AUTHENTICITY_MSG="""
paramiko: The authenticity of host '%s' can't be established.
The %s key fingerprint is %s.
Are you sure you want to continue connecting (yes/no)?
"""
# prevent paramiko warning noise -- see http://stackoverflow.com/questions/3920502/
HAVE_PARAMIKO=False
@ -36,12 +56,58 @@ with warnings.catch_warnings():
except ImportError:
pass
class MyAddPolicy(object):
"""
Based on AutoAddPolicy in paramiko so we can determine when keys are added
and also prompt for input.
Policy for automatically adding the hostname and new host key to the
local L{HostKeys} object, and saving it. This is used by L{SSHClient}.
"""
def __init__(self, runner):
self.runner = runner
def missing_host_key(self, client, hostname, key):
if C.HOST_KEY_CHECKING:
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)
old_stdin = sys.stdin
sys.stdin = self.runner._new_stdin
fingerprint = hexlify(key.get_fingerprint())
ktype = key.get_name()
# clear out any premature input on sys.stdin
tcflush(sys.stdin, TCIFLUSH)
inp = raw_input(AUTHENTICITY_MSG % (hostname, ktype, fingerprint))
sys.stdin = old_stdin
if inp not in ['yes','y','']:
fcntl.flock(self.runner.output_lockfile, fcntl.LOCK_UN)
fcntl.flock(self.runner.process_lockfile, fcntl.LOCK_UN)
raise errors.AnsibleError("host connection rejected by user")
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN)
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
key._added_by_ansible_this_time = True
# existing implementation below:
client._host_keys.add(hostname, key.get_name(), key)
# host keys are actually saved in close() function below
# in order to control ordering.
# keep connection objects on a per host basis to avoid repeated attempts to reconnect
SSH_CONNECTION_CACHE = {}
SFTP_CONNECTION_CACHE = {}
class Connection(object):
''' SSH based connections with Paramiko '''
@ -76,7 +142,12 @@ class Connection(object):
vvv("ESTABLISH CONNECTION FOR USER: %s on PORT %s TO %s" % (self.user, self.port, self.host), host=self.host)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.keyfile = os.path.expanduser("~/.ssh/known_hosts")
if C.HOST_KEY_CHECKING:
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(MyAddPolicy(self.runner))
allow_agent = True
if self.password is not None:
@ -188,6 +259,42 @@ class Connection(object):
except IOError:
raise errors.AnsibleError("failed to transfer file from %s" % in_path)
def _any_keys_added(self):
added_any = False
for hostname, keys in self.ssh._host_keys.iteritems():
for keytype, key in keys.iteritems():
added_this_time = getattr(key, '_added_by_ansible_this_time', False)
if added_this_time:
return True
return False
def _save_ssh_host_keys(self, filename):
'''
not using the paramiko save_ssh_host_keys function as we want to add new SSH keys at the bottom so folks
don't complain about it :)
'''
if not self._any_keys_added():
return False
path = os.path.expanduser("~/.ssh")
if not os.path.exists(path):
os.makedirs(path)
f = open(filename, 'w')
for hostname, keys in self.ssh._host_keys.iteritems():
for keytype, key in keys.iteritems():
# was f.write
added_this_time = getattr(key, '_added_by_ansible_this_time', False)
if not added_this_time:
f.write("%s %s %s\n" % (hostname, keytype, key.get_base64()))
for hostname, keys in self.ssh._host_keys.iteritems():
for keytype, key in keys.iteritems():
added_this_time = getattr(key, '_added_by_ansible_this_time', False)
if added_this_time:
f.write("%s %s %s\n" % (hostname, keytype, key.get_base64()))
f.close()
def close(self):
''' terminate the connection '''
cache_key = self._cache_key()
@ -195,5 +302,28 @@ class Connection(object):
SFTP_CONNECTION_CACHE.pop(cache_key, None)
if self.sftp is not None:
self.sftp.close()
if C.PARAMIKO_RECORD_HOST_KEYS and self._any_keys_added():
# add any new SSH host keys -- warning -- this could be slow
lockfile = self.keyfile.replace("known_hosts",".known_hosts.lock")
dirname = os.path.dirname(self.keyfile)
if not os.path.exists(dirname):
os.makedirs(dirname)
KEY_LOCK = open(lockfile, 'w')
fcntl.lockf(KEY_LOCK, fcntl.LOCK_EX)
try:
# just in case any were added recently
self.ssh.load_system_host_keys()
self.ssh._host_keys.update(self.ssh._system_host_keys)
self._save_ssh_host_keys(self.keyfile)
except:
# unable to save keys, including scenario when key was invalid
# and caught earlier
traceback.print_exc()
pass
fcntl.lockf(KEY_LOCK, fcntl.LOCK_UN)
self.ssh.close()

View File

@ -23,7 +23,9 @@ import pipes
import random
import select
import fcntl
import hmac
import pwd
from hashlib import sha1
import ansible.constants as C
from ansible.callbacks import vvv
from ansible import errors
@ -39,6 +41,8 @@ class Connection(object):
self.user = user
self.password = password
self.private_key_file = private_key_file
self.cp_dir = utils.prepare_writeable_dir('$HOME/.ansible/cp',mode=0700)
self.HASHED_KEY_MAGIC = "|1|"
def connect(self):
''' connect to the remote host '''
@ -52,8 +56,22 @@ class Connection(object):
else:
self.common_args += ["-o", "ControlMaster=auto",
"-o", "ControlPersist=60s",
"-o", "ControlPath=/tmp/ansible-ssh-%h-%p-%r"]
self.common_args += ["-o", "StrictHostKeyChecking=no"]
"-o", "ControlPath=%s/ansible-ssh-%%h-%%p-%%r" % self.cp_dir]
cp_in_use = False
cp_path_set = False
for arg in self.common_args:
if arg.find("ControlPersist") != -1:
cp_in_use = True
if arg.find("ControlPath") != -1:
cp_path_set = True
if cp_in_use and not cp_path_set:
self.common_args += ["-o", "ControlPath=%s/ansible-ssh-%%h-%%p-%%r" % self.cp_dir]
if not C.HOST_KEY_CHECKING:
self.common_args += ["-o", "StrictHostKeyChecking=no"]
if self.port is not None:
self.common_args += ["-o", "Port=%d" % (self.port)]
if self.private_key_file is not None:
@ -90,6 +108,35 @@ class Connection(object):
os.write(self.wfd, "%s\n" % self.password)
os.close(self.wfd)
def not_in_host_file(self, host):
host_file = os.path.expanduser("~/.ssh/known_hosts")
if not os.path.exists(host_file):
print "previous known host file not found"
return True
host_fh = open(host_file)
data = host_fh.read()
host_fh.close()
for line in data.split("\n"):
if line is None or line.find(" ") == -1:
continue
tokens = line.split()
if tokens[0].find(self.HASHED_KEY_MAGIC) == 0:
# this is a hashed known host entry
try:
(kn_salt,kn_host) = tokens[0][len(self.HASHED_KEY_MAGIC):].split("|",2)
hash = hmac.new(kn_salt.decode('base64'), digestmod=sha1)
hash.update(host)
if hash.digest() == kn_host.decode('base64'):
return False
except:
# invalid hashed host key, skip it
continue
else:
# standard host file entry
if host in tokens[0]:
return False
return True
def exec_command(self, cmd, tmp_path, sudo_user,sudoable=False, executable='/bin/sh'):
''' run a command on the remote host '''
@ -106,6 +153,17 @@ class Connection(object):
ssh_cmd.append(sudocmd)
vvv("EXEC %s" % ssh_cmd, host=self.host)
not_in_host_file = self.not_in_host_file(self.host)
if C.HOST_KEY_CHECKING and not_in_host_file:
# lock around the initial SSH connectivity so the user prompt about whether to add
# the host to known hosts is not intermingled with multiprocess output.
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)
try:
# Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors
import pty
@ -158,6 +216,12 @@ class Connection(object):
elif p.poll() is not None:
break
stdin.close() # close stdin after we read from stdout (see also issue #848)
if C.HOST_KEY_CHECKING and not_in_host_file:
# lock around the initial SSH connectivity so the user prompt about whether to add
# the host to known hosts is not intermingled with multiprocess output.
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN)
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
if p.returncode != 0 and stderr.find('Bad configuration option: ControlPersist') != -1:
raise errors.AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again')

View File

@ -176,18 +176,34 @@ def is_executable(path):
or stat.S_IXGRP & os.stat(path)[stat.ST_MODE]
or stat.S_IXOTH & os.stat(path)[stat.ST_MODE])
def prepare_writeable_dir(tree):
def unfrackpath(path):
'''
returns a path that is free of symlinks, environment
variables, relative path traversals and symbols (~)
example:
'$HOME/../../var/mail' becomes '/var/spool/mail'
'''
return os.path.normpath(os.path.realpath(os.path.expandvars(os.path.expanduser(path))))
def prepare_writeable_dir(tree,mode=0777):
''' make sure a directory exists and is writeable '''
if tree != '/':
tree = os.path.realpath(os.path.expanduser(tree))
# modify the mode to ensure the owner at least
# has read/write access to this directory
mode |= 0700
# make sure the tree path is always expanded
# and normalized and free of symlinks
tree = unfrackpath(tree)
if not os.path.exists(tree):
try:
os.makedirs(tree)
os.makedirs(tree, mode)
except (IOError, OSError), e:
exit("Could not make dir %s: %s" % (tree, e))
if not os.access(tree, os.W_OK):
exit("Cannot write to path %s" % tree)
return tree
def path_dwim(basedir, given):
'''

View File

@ -19,7 +19,7 @@ import os
import sys
import glob
import imp
import ansible.constants as C
from ansible import constants as C
from ansible import errors
MODULE_CACHE = {}

View File

@ -85,7 +85,7 @@ def main():
module.exit_json(changed=False, name=name, state=state)
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
running = 'Running' in out
running = 'running' in out.lower()
if running and (state == 'started' or state == 'monitored'):
module.exit_json(changed=False, name=name, state=state)

View File

@ -995,6 +995,9 @@ class AIX(Hardware):
rc, out, err = module.run_command("/usr/sbin/lsdev -Cc processor")
if not out:
# CPU info may not be available (WPAR)
return
i = 0
for line in out.split('\n'):
@ -1032,6 +1035,9 @@ class AIX(Hardware):
# /dev/ada0p3 314368 0 314368 0%
#
rc, out, err = module.run_command("/usr/sbin/lsps -s")
if not out:
# swapinfo may not be available (WPAR)
return
lines = out.split('\n')
data = lines[1].split()
swaptotal_mb = long(data[0].rstrip('MB'))

View File

@ -1,3 +1,21 @@
ansible (1.2.3) unstable; urgency=high
* 1.2.3 release
-- James Cammarata <jcammarata@ansibleworks.com> Wed, 21 August 2013 13:00:00 -0500
ansible (1.2.2) unstable; urgency=low
* 1.2.2 release
-- Michael DeHaan <michael.dehaan@gmail.com> Thu, 05 July 2013 21:45:00 -0500
ansible (1.2.1) unstable; urgency=low
* 1.2.1 release
-- Michael DeHaan <michael.dehaan@gmail.com> Thu, 04 July 2013 21:45:00 -0500
ansible (1.2) unstable; urgency=low
* 1.2 release

View File

@ -6,7 +6,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
Name: ansible
Release: 1%{?dist}
Summary: SSH-based configuration management, deployment, and orchestration engine
Version: 1.2
Version: 1.2.3
Group: Development/Libraries
License: GPLv3
@ -116,6 +116,15 @@ rm -rf $RPM_BUILD_ROOT
%changelog
* Wed Aug 21 2013 James Cammarata <jcammarata@ansibleworks.com> - 1.2-3
* Release 1.2.3
* Thu Jul 05 2013 Michael DeHaan <michael.dehaan@gmail.com> - 1.2-2
* Release 1.2.2
* Thu Jul 04 2013 Michael DeHaan <michael.dehaan@gmail.com> - 1.2-1
* Release 1.2.1
* Mon Jun 10 2013 Michael DeHaan <michael.dehaan@gmail.com> - 1.2-0
* Release 1.2