From 9213cf896ef3a2ff197af7683f2f750b6c613353 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 10 Apr 2012 13:20:03 -0700 Subject: [PATCH 1/8] Change to transport is local *and* is localhost Connection.connect() now requires that, in order to use LocalConnection, you specify transport is local and that the hostname is localhost. --- lib/ansible/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ansible/connection.py b/lib/ansible/connection.py index 11faac402bd..a01c7ae4cb7 100755 --- a/lib/ansible/connection.py +++ b/lib/ansible/connection.py @@ -41,7 +41,7 @@ class Connection(object): def connect(self, host): conn = None - if self.transport == 'local' or self._LOCALHOSTRE.search(host): + if self.transport == 'local' and self._LOCALHOSTRE.search(host): conn = LocalConnection(self.runner, host) elif self.transport == 'paramiko': conn = ParamikoConnection(self.runner, host) From 896f8de446617cae76de85f70d56bb050609690d Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 10 Apr 2012 16:09:20 -0700 Subject: [PATCH 2/8] Add DEFAULT_TRANSPORT and DEFAULT_TRANSPORT_OPTS constants DEFAULT_TRANSPORT is set to paramiko. DEFAULT_TRANSPORT_OPTS is a list of possible transport options; it is set to local and paramiko. --- lib/ansible/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 7215f23b373..751d48488dd 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -33,3 +33,5 @@ DEFAULT_POLL_INTERVAL = 15 DEFAULT_REMOTE_USER = 'root' DEFAULT_REMOTE_PASS = None DEFAULT_REMOTE_PORT = 22 +DEFAULT_TRANSPORT = 'paramiko' +DEFAULT_TRANSPORT_OPTS = ['local', 'paramiko'] From 13914815239d6c0291146566dfe0f04a55dc909c Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 10 Apr 2012 16:13:18 -0700 Subject: [PATCH 3/8] Add support for -c, --connection argument to specify connection type Adds support to specify connection type to use. The option -c, --connection is added when connect_opts is set to True. connect_opts is added to make_parser() and base_parser_options(). --- lib/ansible/utils.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/ansible/utils.py b/lib/ansible/utils.py index 5eb5d07e5d0..fa13120922e 100755 --- a/lib/ansible/utils.py +++ b/lib/ansible/utils.py @@ -273,14 +273,15 @@ def parse_kv(args): options[k]=v return options -def make_parser(add_options, constants=C, usage="", output_opts=False, runas_opts=False, async_opts=False): +def make_parser(add_options, constants=C, usage="", output_opts=False, runas_opts=False, async_opts=False, connect_opts=False): ''' create an options parser w/ common options for any ansible program ''' options = base_parser_options( constants=constants, output_opts=output_opts, runas_opts=runas_opts, - async_opts=async_opts + async_opts=async_opts, + connect_opts=connect_opts ) options.update(add_options) @@ -293,7 +294,7 @@ def make_parser(add_options, constants=C, usage="", output_opts=False, runas_opt parser.add_option(n, long, **data) return parser -def base_parser_options(constants=C, output_opts=False, runas_opts=False, async_opts=False): +def base_parser_options(constants=C, output_opts=False, runas_opts=False, async_opts=False, connect_opts=False): ''' creates common options for ansible programs ''' options = { @@ -329,6 +330,14 @@ def base_parser_options(constants=C, output_opts=False, runas_opts=False, async_ dest='remote_user', help='connect as this user'), }) + if connect_opts: + options.update({ + '-c' : dict(long='--connection', dest='connection', + choices=C.DEFAULT_TRANSPORT_OPTS, + default=C.DEFAULT_TRANSPORT, + help="connection type to use") + }) + if async_opts: options.update({ '-P' : dict(long='--poll', default=constants.DEFAULT_POLL_INTERVAL, type='int', From 5d74fedeb9ebee6fc16fc3646bc8ebb420a5e955 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 10 Apr 2012 16:17:39 -0700 Subject: [PATCH 4/8] Update Runner to default to C.DEFAULT_TRANSPORT This also uses self.transport when instantiating Connection object. --- lib/ansible/runner.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index b2cf3518b99..2093cac655a 100755 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -73,7 +73,7 @@ class Runner(object): forks=C.DEFAULT_FORKS, timeout=C.DEFAULT_TIMEOUT, pattern=C.DEFAULT_PATTERN, remote_user=C.DEFAULT_REMOTE_USER, remote_pass=C.DEFAULT_REMOTE_PASS, remote_port=C.DEFAULT_REMOTE_PORT, background=0, basedir=None, setup_cache=None, - transport='paramiko', conditional='True', groups={}, callbacks=None, verbose=False, + transport=C.DEFAULT_TRANSPORT, conditional='True', groups={}, callbacks=None, verbose=False, debug=False, sudo=False, extra_vars=None, module_vars=None): if setup_cache is None: @@ -86,7 +86,9 @@ class Runner(object): self.callbacks = callbacks self.generated_jid = str(random.randint(0, 999999999999)) - self.connector = ansible.connection.Connection(self, transport) + + self.transport = transport + self.connector = ansible.connection.Connection(self, self.transport) if type(host_list) == str: self.host_list, self.groups = self.parse_hosts(host_list) From b5061bb62e63b73994b46682bb76559ccb16a119 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 10 Apr 2012 16:27:19 -0700 Subject: [PATCH 5/8] Verify that effective uid == remote_user when transport is local Raise exception if effective uid of process is not the same as remote_user. --- lib/ansible/runner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index 2093cac655a..2e14b517bf5 100755 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -22,6 +22,7 @@ import fnmatch import multiprocessing import signal import os +import pwd import Queue import random import traceback @@ -115,6 +116,9 @@ class Runner(object): self.basedir = basedir self.sudo = sudo + euid = pwd.getpwuid(os.geteuid())[0] + if self.transport == 'local' and self.remote_user != euid: + raise Exception("User mismatch: expected %s, but is %s" % (self.remote_user, euid)) if type(self.module_args) != str: raise Exception("module_args must be a string: %s" % self.module_args) From fdee1d3459a7127bef97716bc9f66838b76381a5 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Tue, 10 Apr 2012 16:28:43 -0700 Subject: [PATCH 6/8] Add support to bin/ansible for --connection option --- bin/ansible | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/ansible b/bin/ansible index 4d0a3b9c4b6..9acdb307d27 100755 --- a/bin/ansible +++ b/bin/ansible @@ -61,6 +61,7 @@ class Cli(object): runas_opts=True, async_opts=True, output_opts=True, + connect_opts=True, ) @@ -97,7 +98,7 @@ class Cli(object): remote_port=options.remote_port, forks=options.forks, background=options.seconds, pattern=pattern, callbacks=self.callbacks, sudo=options.sudo, verbose=True, - debug=options.debug + transport=options.connection, debug=options.debug ) return (runner, runner.run()) From b506567c379ab4bf2c33a0f5194bc5204ee3d06d Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Wed, 11 Apr 2012 09:32:29 -0700 Subject: [PATCH 7/8] Update ansible.1 manpage for option -c, --connection --- docs/man/man1/ansible.1 | 12 ++++++++++-- docs/man/man1/ansible.1.asciidoc | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/man/man1/ansible.1 b/docs/man/man1/ansible.1 index 1e61d7ce984..8d76a70a0cb 100644 --- a/docs/man/man1/ansible.1 +++ b/docs/man/man1/ansible.1 @@ -2,12 +2,12 @@ .\" Title: ansible .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.76.1 -.\" Date: 04/03/2012 +.\" Date: 04/10/2012 .\" Manual: System administration commands .\" Source: Ansible 0.0.2 .\" Language: English .\" -.TH "ANSIBLE" "1" "04/03/2012" "Ansible 0\&.0\&.2" "System administration commands" +.TH "ANSIBLE" "1" "04/10/2012" "Ansible 0\&.0\&.2" "System administration commands" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -122,6 +122,14 @@ Use this remote \fIUSERNAME\fR instead of root\&. .RE +.PP +\fB\-c\fR \fICONNECTION\fR, \fB\-\-connection=\fR\fICONNECTION\fR +.RS 4 +Connection type to use\&. Possible options are +\fIparamiko\fR +and +\fIlocal\fR\&. +.RE .SH "INVENTORY" .sp Ansible stores the hosts it can potentially operate on in an inventory file\&. The syntax is one host per line\&. Groups headers are allowed and are included on their own line, enclosed in square brackets\&. diff --git a/docs/man/man1/ansible.1.asciidoc b/docs/man/man1/ansible.1.asciidoc index 6d08394e820..11ce91f6d46 100644 --- a/docs/man/man1/ansible.1.asciidoc +++ b/docs/man/man1/ansible.1.asciidoc @@ -96,6 +96,10 @@ Poll a background job every 'NUM' seconds. Requires *-B*. Use this remote 'USERNAME' instead of root. +*-c* 'CONNECTION', *--connection=*'CONNECTION':: + +Connection type to use. Possible options are 'paramiko' and 'local'. + INVENTORY --------- From 40fc9a3249de391de304bcbd77618186d1488d86 Mon Sep 17 00:00:00 2001 From: Stephen Fromm Date: Wed, 11 Apr 2012 09:39:04 -0700 Subject: [PATCH 8/8] Update playbook to be transport aware This adds transport variable to playbook.py. It can be set with 'connection' in the playbook file. --- lib/ansible/playbook.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/ansible/playbook.py b/lib/ansible/playbook.py index 0eb0e4d685c..8cce354b692 100755 --- a/lib/ansible/playbook.py +++ b/lib/ansible/playbook.py @@ -55,6 +55,7 @@ class PlayBook(object): remote_user = C.DEFAULT_REMOTE_USER, remote_pass = C.DEFAULT_REMOTE_PASS, remote_port = C.DEFAULT_REMOTE_PORT, + transport = C.DEFAULT_TRANSPORT, override_hosts = None, extra_vars = None, debug = False, @@ -73,6 +74,7 @@ class PlayBook(object): self.remote_user = remote_user self.remote_pass = remote_pass self.remote_port = remote_port + self.transport = transport self.debug = debug self.verbose = verbose self.callbacks = callbacks @@ -272,7 +274,7 @@ class PlayBook(object): # ***************************************************** def _run_module(self, pattern, host_list, module, args, vars, remote_user, - async_seconds, async_poll_interval, only_if, sudo): + async_seconds, async_poll_interval, only_if, sudo, transport): ''' run a particular module step in a playbook ''' hosts = [ h for h in host_list if (h not in self.stats.failures) and (h not in self.stats.dark)] @@ -285,7 +287,8 @@ class PlayBook(object): remote_port=self.remote_port, module_vars=vars, setup_cache=SETUP_CACHE, basedir=self.basedir, conditional=only_if, callbacks=self.runner_callbacks, - extra_vars=self.extra_vars, debug=self.debug, sudo=sudo + extra_vars=self.extra_vars, debug=self.debug, sudo=sudo, + transport=transport ) if async_seconds == 0: @@ -296,7 +299,7 @@ class PlayBook(object): # ***************************************************** def _run_task(self, pattern=None, host_list=None, task=None, - remote_user=None, handlers=None, conditional=False, sudo=False): + remote_user=None, handlers=None, conditional=False, sudo=False, transport=None): ''' run a single task in the playbook and recursively run any subtasks. ''' # load the module name and parameters from the task entry @@ -328,7 +331,7 @@ class PlayBook(object): # run the task in parallel results = self._run_module(pattern, host_list, module_name, module_args, module_vars, remote_user, async_seconds, - async_poll_interval, only_if, sudo) + async_poll_interval, only_if, sudo, transport) self.stats.compute(results) @@ -423,7 +426,7 @@ class PlayBook(object): # ***************************************************** - def _do_setup_step(self, pattern, vars, user, port, sudo, vars_files=None): + def _do_setup_step(self, pattern, vars, user, port, sudo, transport, vars_files=None): ''' push variables down to the systems and get variables+facts back up ''' # this enables conditional includes like $facter_os.yml and is only done @@ -454,6 +457,7 @@ class PlayBook(object): remote_pass=self.remote_pass, remote_port=self.remote_port, setup_cache=SETUP_CACHE, callbacks=self.runner_callbacks, sudo=sudo, + transport=transport, ).run() self.stats.compute(setup_results, setup=True) @@ -493,15 +497,16 @@ class PlayBook(object): user = pg.get('user', self.remote_user) port = pg.get('port', self.remote_port) sudo = pg.get('sudo', False) + transport = pg.get('connection', self.transport) self.callbacks.on_play_start(pattern) # push any variables down to the system # and get facts/ohai/other data back up - self._do_setup_step(pattern, vars, user, port, sudo, None) + self._do_setup_step(pattern, vars, user, port, sudo, transport, None) # now with that data, handle contentional variable file imports! if len(vars_files) > 0: - self._do_setup_step(pattern, vars, user, port, sudo, vars_files) + self._do_setup_step(pattern, vars, user, port, sudo, transport, vars_files) # run all the top level tasks, these get run on every node for task in tasks: @@ -511,7 +516,8 @@ class PlayBook(object): task=task, handlers=handlers, remote_user=user, - sudo=sudo + sudo=sudo, + transport=transport ) # handlers only run on certain nodes, they are flagged by _flag_handlers @@ -530,7 +536,8 @@ class PlayBook(object): host_list=triggered_by, conditional=True, remote_user=user, - sudo=sudo + sudo=sudo, + transport=transport ) # end of execution for this particular pattern. Multiple patterns