From 8c08f1b3024ccbacfc4fee3e8d77c9a31b291feb Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Thu, 23 Apr 2015 18:54:48 -0500 Subject: [PATCH] Updating connection plugins not yet updated in v2 to catch new code --- v2/ansible/plugins/connections/accelerate.py | 19 +- v2/ansible/plugins/connections/chroot.py | 12 +- v2/ansible/plugins/connections/funcd.py | 11 +- v2/ansible/plugins/connections/jail.py | 12 +- v2/ansible/plugins/connections/libvirt_lxc.py | 12 +- .../plugins/connections/paramiko_ssh.py | 55 +++--- v2/ansible/plugins/connections/winrm.py | 59 ++++--- v2/ansible/plugins/connections/zone.py | 162 ++++++++++++++++++ 8 files changed, 255 insertions(+), 87 deletions(-) create mode 100644 v2/ansible/plugins/connections/zone.py diff --git a/v2/ansible/plugins/connections/accelerate.py b/v2/ansible/plugins/connections/accelerate.py index 78e2630eff..0627267c16 100644 --- a/v2/ansible/plugins/connections/accelerate.py +++ b/v2/ansible/plugins/connections/accelerate.py @@ -14,8 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type import json import os @@ -52,6 +50,7 @@ class Connection(object): self.accport = port[1] self.is_connected = False self.has_pipelining = False + self.become_methods_supported=['sudo'] if not self.port: self.port = constants.DEFAULT_REMOTE_PORT @@ -142,7 +141,7 @@ class Connection(object): # shutdown, so we'll reconnect. wrong_user = True - except AnsibleError as e: + except AnsibleError, e: if allow_ssh: if "WRONG_USER" in e: vvv("Switching users, waiting for the daemon on %s to shutdown completely..." % self.host) @@ -228,11 +227,11 @@ class Connection(object): else: return response.get('rc') == 0 - def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' - if su or su_user: - raise AnsibleError("Internal Error: this module does not support running commands via su") + if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") @@ -240,8 +239,8 @@ class Connection(object): if executable == "": executable = constants.DEFAULT_EXECUTABLE - if self.runner.sudo and sudoable and sudo_user: - cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd) + if self.runner.become and sudoable: + cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) vvv("EXEC COMMAND %s" % cmd) @@ -294,8 +293,8 @@ class Connection(object): if fd.tell() >= fstat.st_size: last = True data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last) - if self.runner.sudo: - data['user'] = self.runner.sudo_user + if self.runner.become: + data['user'] = self.runner.become_user data = utils.jsonify(data) data = utils.encrypt(self.key, data) diff --git a/v2/ansible/plugins/connections/chroot.py b/v2/ansible/plugins/connections/chroot.py index 4e61f4ea55..3e96047287 100644 --- a/v2/ansible/plugins/connections/chroot.py +++ b/v2/ansible/plugins/connections/chroot.py @@ -15,8 +15,6 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type import distutils.spawn import traceback @@ -26,6 +24,7 @@ import subprocess from ansible import errors from ansible import utils from ansible.callbacks import vvv +import ansible.constants as C class Connection(object): ''' Local chroot based connections ''' @@ -33,6 +32,7 @@ class Connection(object): def __init__(self, runner, host, port, *args, **kwargs): self.chroot = host self.has_pipelining = False + self.become_methods_supported=C.BECOME_METHODS if os.geteuid() != 0: raise errors.AnsibleError("chroot connection requires running as root") @@ -62,16 +62,16 @@ class Connection(object): return self - def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the chroot ''' - if su or su_user: - raise errors.AnsibleError("Internal Error: this module does not support running commands via su") + if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") - # We enter chroot as root so sudo stuff can be ignored + # We enter chroot as root so we ignore privlege escalation? if executable: local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd] diff --git a/v2/ansible/plugins/connections/funcd.py b/v2/ansible/plugins/connections/funcd.py index 83a0c9b01d..92b7f53605 100644 --- a/v2/ansible/plugins/connections/funcd.py +++ b/v2/ansible/plugins/connections/funcd.py @@ -18,9 +18,6 @@ # along with Ansible. If not, see . # --- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - # The func transport permit to use ansible over func. For people who have already setup # func and that wish to play with ansible, this permit to move gradually to ansible # without having to redo completely the setup of the network. @@ -56,16 +53,14 @@ class Connection(object): self.client = fc.Client(self.host) return self - def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, - executable='/bin/sh', in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, + executable='/bin/sh', in_data=None): ''' run a command on the remote minion ''' - if su or su_user: - raise errors.AnsibleError("Internal Error: this module does not support running commands via su") - if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") + # totally ignores privlege escalation vvv("EXEC %s" % (cmd), host=self.host) p = self.client.command.run(cmd)[self.host] return (p[0], '', p[1], p[2]) diff --git a/v2/ansible/plugins/connections/jail.py b/v2/ansible/plugins/connections/jail.py index a81f587bfd..c7b61bc638 100644 --- a/v2/ansible/plugins/connections/jail.py +++ b/v2/ansible/plugins/connections/jail.py @@ -16,8 +16,6 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type import distutils.spawn import traceback @@ -26,6 +24,7 @@ import shutil import subprocess from ansible import errors from ansible.callbacks import vvv +import ansible.constants as C class Connection(object): ''' Local chroot based connections ''' @@ -63,6 +62,7 @@ class Connection(object): self.runner = runner self.host = host self.has_pipelining = False + self.become_methods_supported=C.BECOME_METHODS if os.geteuid() != 0: raise errors.AnsibleError("jail connection requires running as root") @@ -93,16 +93,16 @@ class Connection(object): local_cmd = '%s "%s" %s' % (self.jexec_cmd, self.jail, cmd) return local_cmd - def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the chroot ''' - if su or su_user: - raise errors.AnsibleError("Internal Error: this module does not support running commands via su") + if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") - # We enter chroot as root so sudo stuff can be ignored + # Ignores privilege escalation local_cmd = self._generate_cmd(executable, cmd) vvv("EXEC %s" % (local_cmd), host=self.jail) diff --git a/v2/ansible/plugins/connections/libvirt_lxc.py b/v2/ansible/plugins/connections/libvirt_lxc.py index ee824554a0..34cdb592b2 100644 --- a/v2/ansible/plugins/connections/libvirt_lxc.py +++ b/v2/ansible/plugins/connections/libvirt_lxc.py @@ -16,14 +16,13 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type import distutils.spawn import os import subprocess from ansible import errors from ansible.callbacks import vvv +import ansible.constants as C class Connection(object): ''' Local lxc based connections ''' @@ -52,6 +51,7 @@ class Connection(object): self.host = host # port is unused, since this is local self.port = port + self.become_methods_supported=C.BECOME_METHODS def connect(self, port=None): ''' connect to the lxc; nothing to do here ''' @@ -67,16 +67,16 @@ class Connection(object): local_cmd = '%s -q -c lxc:/// lxc-enter-namespace %s -- %s' % (self.cmd, self.lxc, cmd) return local_cmd - def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the chroot ''' - if su or su_user: - raise errors.AnsibleError("Internal Error: this module does not support running commands via su") + if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") - # We enter lxc as root so sudo stuff can be ignored + # We ignore privelege escalation! local_cmd = self._generate_cmd(executable, cmd) vvv("EXEC %s" % (local_cmd), host=self.lxc) diff --git a/v2/ansible/plugins/connections/paramiko_ssh.py b/v2/ansible/plugins/connections/paramiko_ssh.py index 167b0d39a8..8eaf97c3f6 100644 --- a/v2/ansible/plugins/connections/paramiko_ssh.py +++ b/v2/ansible/plugins/connections/paramiko_ssh.py @@ -14,8 +14,7 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type + # --- # The paramiko transport is provided because many distributions, in particular EL6 and before @@ -126,6 +125,9 @@ class Connection(object): self.private_key_file = private_key_file self.has_pipelining = False + # TODO: add pbrun, pfexec + self.become_methods_supported=['sudo', 'su', 'pbrun'] + def _cache_key(self): return "%s__%s__" % (self.host, self.user) @@ -171,7 +173,7 @@ class Connection(object): key_filename=key_filename, password=self.password, timeout=self.runner.timeout, port=self.port) - except Exception as e: + except Exception, e: msg = str(e) if "PID check failed" in msg: @@ -185,9 +187,12 @@ class Connection(object): return ssh - def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' + if self.runner.become and sudoable and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) + if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") @@ -198,7 +203,7 @@ class Connection(object): self.ssh.get_transport().set_keepalive(5) chan = self.ssh.get_transport().open_session() - except Exception as e: + except Exception, e: msg = "Failed to open session" if len(str(e)) > 0: @@ -207,7 +212,7 @@ class Connection(object): no_prompt_out = '' no_prompt_err = '' - if not (self.runner.sudo and sudoable) and not (self.runner.su and su): + if not (self.runner.become and sudoable): if executable: quoted_command = executable + ' -c ' + pipes.quote(cmd) @@ -225,50 +230,46 @@ class Connection(object): chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) - if self.runner.sudo or sudoable: - shcmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd) - elif self.runner.su or su: - shcmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) + if self.runner.become and sudoable: + shcmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) vvv("EXEC %s" % shcmd, host=self.host) - sudo_output = '' + become_output = '' try: chan.exec_command(shcmd) - if self.runner.sudo_pass or self.runner.su_pass: + if self.runner.become_pass: while True: - if success_key in sudo_output or \ - (self.runner.sudo_pass and sudo_output.endswith(prompt)) or \ - (self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)): + if success_key in become_output or \ + (prompt and become_output.endswith(prompt)) or \ + utils.su_prompts.check_su_prompt(become_output): break chunk = chan.recv(bufsize) if not chunk: - if 'unknown user' in sudo_output: + if 'unknown user' in become_output: raise errors.AnsibleError( - 'user %s does not exist' % sudo_user) + 'user %s does not exist' % become_user) else: raise errors.AnsibleError('ssh connection ' + 'closed waiting for password prompt') - sudo_output += chunk + become_output += chunk - if success_key not in sudo_output: + if success_key not in become_output: if sudoable: - chan.sendall(self.runner.sudo_pass + '\n') - elif su: - chan.sendall(self.runner.su_pass + '\n') + chan.sendall(self.runner.become_pass + '\n') else: - no_prompt_out += sudo_output - no_prompt_err += sudo_output + no_prompt_out += become_output + no_prompt_err += become_output except socket.timeout: - raise errors.AnsibleError('ssh timed out waiting for sudo.\n' + sudo_output) + raise errors.AnsibleError('ssh timed out waiting for privilege escalation.\n' + become_output) stdout = ''.join(chan.makefile('rb', bufsize)) stderr = ''.join(chan.makefile_stderr('rb', bufsize)) @@ -285,7 +286,7 @@ class Connection(object): try: self.sftp = self.ssh.open_sftp() - except Exception as e: + except Exception, e: raise errors.AnsibleError("failed to open a SFTP connection (%s)" % e) try: @@ -309,7 +310,7 @@ class Connection(object): try: self.sftp = self._connect_sftp() - except Exception as e: + except Exception, e: raise errors.AnsibleError("failed to open a SFTP connection (%s)", e) try: diff --git a/v2/ansible/plugins/connections/winrm.py b/v2/ansible/plugins/connections/winrm.py index f3d6a03ba0..b41a74c8e1 100644 --- a/v2/ansible/plugins/connections/winrm.py +++ b/v2/ansible/plugins/connections/winrm.py @@ -14,18 +14,15 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type + +from __future__ import absolute_import import base64 -import hashlib -import imp import os import re import shlex import traceback - -from six.moves.urllib import parse as urlparse +import urlparse from ansible import errors from ansible import utils from ansible.callbacks import vvv, vvvv, verbose @@ -38,9 +35,12 @@ try: except ImportError: raise errors.AnsibleError("winrm is not installed") -_winrm_cache = { - # 'user:pwhash@host:port': -} +HAVE_KERBEROS = False +try: + import kerberos + HAVE_KERBEROS = True +except ImportError: + pass def vvvvv(msg, host=None): verbose(msg, host=host, caplevel=4) @@ -48,6 +48,11 @@ def vvvvv(msg, host=None): class Connection(object): '''WinRM connections over HTTP/HTTPS.''' + transport_schemes = { + 'http': [('kerberos', 'http'), ('plaintext', 'http'), ('plaintext', 'https')], + 'https': [('kerberos', 'https'), ('plaintext', 'https')], + } + def __init__(self, runner, host, port, user, password, *args, **kwargs): self.runner = runner self.host = host @@ -61,6 +66,10 @@ class Connection(object): self.shell_id = None self.delegate = None + # Add runas support + #self.become_methods_supported=['runas'] + self.become_methods_supported=[] + def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. @@ -69,23 +78,22 @@ class Connection(object): vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) - cache_key = '%s:%s@%s:%d' % (self.user, hashlib.md5(self.password).hexdigest(), self.host, port) - if cache_key in _winrm_cache: - vvvv('WINRM REUSE EXISTING CONNECTION: %s' % cache_key, host=self.host) - return _winrm_cache[cache_key] - transport_schemes = [('plaintext', 'https'), ('plaintext', 'http')] # FIXME: ssl/kerberos - if port == 5985: - transport_schemes = reversed(transport_schemes) exc = None - for transport, scheme in transport_schemes: + for transport, scheme in self.transport_schemes['http' if port == 5985 else 'https']: + if transport == 'kerberos' and (not HAVE_KERBEROS or not '@' in self.user): + continue + if transport == 'kerberos': + realm = self.user.split('@', 1)[1].strip() or None + else: + realm = None endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, - username=self.user, password=self.password) + username=self.user, password=self.password, + realm=realm) try: protocol.send_message('') - _winrm_cache[cache_key] = protocol return protocol except WinRMTransportError, exc: err_msg = str(exc) @@ -97,7 +105,6 @@ class Connection(object): if code == 401: raise errors.AnsibleError("the username/password specified for this server was incorrect") elif code == 411: - _winrm_cache[cache_key] = protocol return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue @@ -133,7 +140,11 @@ class Connection(object): self.protocol = self._winrm_connect() return self - def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable=None, in_data=None, su=None, su_user=None): + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None): + + if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) + cmd = cmd.encode('utf-8') cmd_parts = shlex.split(cmd, posix=False) if '-EncodedCommand' in cmd_parts: @@ -144,11 +155,11 @@ class Connection(object): vvv("EXEC %s" % cmd, host=self.host) # For script/raw support. if cmd_parts and cmd_parts[0].lower().endswith('.ps1'): - script = powershell._build_file_cmd(cmd_parts) + script = powershell._build_file_cmd(cmd_parts, quote_args=False) cmd_parts = powershell._encode_script(script, as_list=True) try: result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True) - except Exception as e: + except Exception, e: traceback.print_exc() raise errors.AnsibleError("failed to exec cmd %s" % cmd) return (result.status_code, '', result.std_out.encode('utf-8'), result.std_err.encode('utf-8')) @@ -194,7 +205,7 @@ class Connection(object): def fetch_file(self, in_path, out_path): out_path = out_path.replace('\\', '/') vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) - buffer_size = 2**20 # 1MB chunks + buffer_size = 2**19 # 0.5MB chunks if not os.path.exists(os.path.dirname(out_path)): os.makedirs(os.path.dirname(out_path)) out_file = None diff --git a/v2/ansible/plugins/connections/zone.py b/v2/ansible/plugins/connections/zone.py new file mode 100644 index 0000000000..211bd0fbcc --- /dev/null +++ b/v2/ansible/plugins/connections/zone.py @@ -0,0 +1,162 @@ +# Based on local.py (c) 2012, Michael DeHaan +# and chroot.py (c) 2013, Maykel Moya +# and jail.py (c) 2013, Michael Scherer +# (c) 2015, Dagobert Michelsen +# +# 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 . + +import distutils.spawn +import traceback +import os +import shutil +import subprocess +from subprocess import Popen,PIPE +from ansible import errors +from ansible.callbacks import vvv +import ansible.constants as C + +class Connection(object): + ''' Local zone based connections ''' + + def _search_executable(self, executable): + cmd = distutils.spawn.find_executable(executable) + if not cmd: + raise errors.AnsibleError("%s command not found in PATH") % executable + return cmd + + def list_zones(self): + pipe = subprocess.Popen([self.zoneadm_cmd, 'list', '-ip'], + cwd=self.runner.basedir, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + #stdout, stderr = p.communicate() + zones = [] + for l in pipe.stdout.readlines(): + # 1:work:running:/zones/work:3126dc59-9a07-4829-cde9-a816e4c5040e:native:shared + s = l.split(':') + if s[1] != 'global': + zones.append(s[1]) + + return zones + + def get_zone_path(self): + #solaris10vm# zoneadm -z cswbuild list -p + #-:cswbuild:installed:/zones/cswbuild:479f3c4b-d0c6-e97b-cd04-fd58f2c0238e:native:shared + pipe = subprocess.Popen([self.zoneadm_cmd, '-z', self.zone, 'list', '-p'], + cwd=self.runner.basedir, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + #stdout, stderr = p.communicate() + path = pipe.stdout.readlines()[0].split(':')[3] + return path + '/root' + + def __init__(self, runner, host, port, *args, **kwargs): + self.zone = host + self.runner = runner + self.host = host + self.has_pipelining = False + self.become_methods_supported=C.BECOME_METHODS + + if os.geteuid() != 0: + raise errors.AnsibleError("zone connection requires running as root") + + self.zoneadm_cmd = self._search_executable('zoneadm') + self.zlogin_cmd = self._search_executable('zlogin') + + if not self.zone in self.list_zones(): + raise errors.AnsibleError("incorrect zone name %s" % self.zone) + + + self.host = host + # port is unused, since this is local + self.port = port + + def connect(self, port=None): + ''' connect to the zone; nothing to do here ''' + + vvv("THIS IS A LOCAL ZONE DIR", host=self.zone) + + return self + + # a modifier + def _generate_cmd(self, executable, cmd): + if executable: + local_cmd = [self.zlogin_cmd, self.zone, executable, cmd] + else: + local_cmd = '%s "%s" %s' % (self.zlogin_cmd, self.zone, cmd) + return local_cmd + + def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None): + ''' run a command on the zone ''' + + if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: + raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) + + if in_data: + raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") + + # We happily ignore privelege escalation + if executable == '/bin/sh': + executable = None + local_cmd = self._generate_cmd(executable, cmd) + + vvv("EXEC %s" % (local_cmd), host=self.zone) + p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring), + cwd=self.runner.basedir, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, stderr = p.communicate() + return (p.returncode, '', stdout, stderr) + + def _normalize_path(self, path, prefix): + if not path.startswith(os.path.sep): + path = os.path.join(os.path.sep, path) + normpath = os.path.normpath(path) + return os.path.join(prefix, normpath[1:]) + + def _copy_file(self, in_path, out_path): + if not os.path.exists(in_path): + raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) + try: + shutil.copyfile(in_path, out_path) + except shutil.Error: + traceback.print_exc() + raise errors.AnsibleError("failed to copy: %s and %s are the same" % (in_path, out_path)) + except IOError: + traceback.print_exc() + raise errors.AnsibleError("failed to transfer file to %s" % out_path) + + def put_file(self, in_path, out_path): + ''' transfer a file from local to zone ''' + + out_path = self._normalize_path(out_path, self.get_zone_path()) + vvv("PUT %s TO %s" % (in_path, out_path), host=self.zone) + + self._copy_file(in_path, out_path) + + def fetch_file(self, in_path, out_path): + ''' fetch a file from zone to local ''' + + in_path = self._normalize_path(in_path, self.get_zone_path()) + vvv("FETCH %s TO %s" % (in_path, out_path), host=self.zone) + + self._copy_file(in_path, out_path) + + def close(self): + ''' terminate the connection; nothing to do here ''' + pass