Skip to content

Commit

Permalink
Fix problem with jail and zone connection plugins and symlinks from w…
Browse files Browse the repository at this point in the history
…ithin the jail/zone.
  • Loading branch information
abadger committed Jun 19, 2015
1 parent f0777d9 commit ca2f2c4
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 61 deletions.
77 changes: 46 additions & 31 deletions lib/ansible/plugins/connections/jail.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Based on local.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# and chroot.py (c) 2013, Maykel Moya <mmoya@speedyrails.com>
# (c) 2013, Michael Scherer <misc@zarb.org>
# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.com>
#
# This file is part of Ansible
#
Expand All @@ -22,14 +23,15 @@
import distutils.spawn
import traceback
import os
import shutil
import subprocess
from ansible import errors
from ansible.callbacks import vvv
import ansible.constants as C

BUFSIZE = 4096

class Connection(object):
''' Local chroot based connections '''
''' Local BSD Jail based connections '''

def _search_executable(self, executable):
cmd = distutils.spawn.find_executable(executable)
Expand Down Expand Up @@ -81,9 +83,9 @@ def __init__(self, runner, host, port, *args, **kwargs):
self.port = port

def connect(self, port=None):
''' connect to the chroot; nothing to do here '''
''' connect to the jail; nothing to do here '''

vvv("THIS IS A LOCAL CHROOT DIR", host=self.jail)
vvv("THIS IS A LOCAL JAIL DIR", host=self.jail)

return self

Expand All @@ -95,8 +97,14 @@ def _generate_cmd(self, executable, cmd):
local_cmd = '%s "%s" %s' % (self.jexec_cmd, self.jail, cmd)
return local_cmd

def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
''' run a command on the chroot '''
def _buffered_exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None, stdin=subprocess.PIPE):
''' run a command on the jail. This is only needed for implementing
put_file() get_file() so that we don't have to read the whole file
into memory.
compared to exec_command() it looses some niceties like being able to
return the process's exit code immediately.
'''

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)
Expand All @@ -110,45 +118,52 @@ def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executab
vvv("EXEC %s" % (local_cmd), host=self.jail)
p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring),
cwd=self.runner.basedir,
stdin=subprocess.PIPE,
stdin=stdin,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

return p

def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
''' run a command on the jail '''

p = self._buffered_exec_command(cmd, tmp_path, become_user, sudoable, executable, in_data)

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 chroot '''
''' transfer a file from local to jail '''

out_path = self._normalize_path(out_path, self.get_jail_path())
vvv("PUT %s TO %s" % (in_path, out_path), host=self.jail)

self._copy_file(in_path, out_path)
with open(in_path, 'rb') as in_file:
p = self._buffered_exec_command('dd of=%s' % out_path, None, stdin=in_file)
try:
stdout, stderr = p.communicate()
except:
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
if p.returncode != 0:
raise errors.AnsibleError("failed to transfer file to %s:\n%s\n%s" % (out_path, stdout, stderr))

def fetch_file(self, in_path, out_path):
''' fetch a file from chroot to local '''
''' fetch a file from jail to local '''

in_path = self._normalize_path(in_path, self.get_jail_path())
vvv("FETCH %s TO %s" % (in_path, out_path), host=self.jail)

self._copy_file(in_path, out_path)

p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE), None)

with open(out_path, 'wb+') as out_file:
try:
for chunk in p.stdout.read(BUFSIZE):
out_file.write(chunk)
except:
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise errors.AnsibleError("failed to transfer file to %s:\n%s\n%s" % (out_path, stdout, stderr))

def close(self):
''' terminate the connection; nothing to do here '''
Expand Down
77 changes: 47 additions & 30 deletions lib/ansible/plugins/connections/zone.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# and chroot.py (c) 2013, Maykel Moya <mmoya@speedyrails.com>
# and jail.py (c) 2013, Michael Scherer <misc@zarb.org>
# (c) 2015, Dagobert Michelsen <dam@baltic-online.de>
# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.com>
#
# This file is part of Ansible
#
Expand All @@ -23,13 +24,13 @@
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

BUFSIZE = 4096

class Connection(object):
''' Local zone based connections '''

Expand All @@ -44,7 +45,7 @@ def list_zones(self):
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
Expand Down Expand Up @@ -97,13 +98,20 @@ def connect(self, port=None):
# a modifier
def _generate_cmd(self, executable, cmd):
if executable:
### TODO: Why was "-c" removed from here? (vs jail.py)
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 '''
def _buffered_exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None, stdin=subprocess.PIPE):
''' run a command on the zone. This is only needed for implementing
put_file() get_file() so that we don't have to read the whole file
into memory.
compared to exec_command() it looses some niceties like being able to
return the process's exit code immediately.
'''

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)
Expand All @@ -112,52 +120,61 @@ def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executab
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")

# We happily ignore privilege 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,
stdin=stdin,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

return p

def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None):
''' run a command on the zone '''

### TODO: Why all the precautions not to specify /bin/sh? (vs jail.py)
if executable == '/bin/sh':
executable = None

p = self._buffered_exec_command(cmd, tmp_path, become_user, sudoable, executable, in_data)

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)
with open(in_path, 'rb') as in_file:
p = self._buffered_exec_command('dd of=%s' % out_path, None, stdin=in_file)
try:
stdout, stderr = p.communicate()
except:
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
if p.returncode != 0:
raise errors.AnsibleError("failed to transfer file to %s:\n%s\n%s" % (out_path, stdout, stderr))

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)

p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE), None)

with open(out_path, 'wb+') as out_file:
try:
for chunk in p.stdout.read(BUFSIZE):
out_file.write(chunk)
except:
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise errors.AnsibleError("failed to transfer file to %s:\n%s\n%s" % (out_path, stdout, stderr))

def close(self):
''' terminate the connection; nothing to do here '''
Expand Down

0 comments on commit ca2f2c4

Please sign in to comment.