Created
June 10, 2017 19:50
-
-
Save dch/a5274afcaf64733a77022df6cbea8862 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://raw.githubusercontent.com/basarevych/ansible-sshjail/1de8015b5d0f3bb9c9c9569106f35e6fe378decc/sshjail.py | |
# https://github.com/basarevych/ansible-sshjail 1de8015 on Dec 19, 2016 | |
from __future__ import (absolute_import, division, print_function) | |
import os | |
import pipes | |
from ansible.errors import AnsibleError | |
from ansible.plugins.connection.ssh import Connection as SSHConnection | |
from contextlib import contextmanager | |
__metaclass__ = type | |
try: | |
from __main__ import display | |
except ImportError: | |
from ansible.utils.display import Display | |
display = Display() | |
class ConnectionBase(SSHConnection): | |
pass | |
class Connection(ConnectionBase): | |
''' ssh based connections ''' | |
transport = 'sshjail' | |
def __init__(self, *args, **kwargs): | |
super(Connection, self).__init__(*args, **kwargs) | |
# self.host == jailname@jailhost | |
self.inventory_hostname = self.host | |
self.jailspec, self.host = self.host.split('@', 1) | |
# self.jailspec == jailname | |
# self.host == jailhost | |
# this way SSHConnection parent class uses the jailhost as the SSH remote host | |
# jail information loaded on first use by match_jail | |
self.jid = None | |
self.jname = None | |
self.jpath = None | |
self.connector = None | |
# logging.warning(self._play_context.connection) | |
def match_jail(self): | |
if self.jid is None: | |
code, stdout, stderr = self._jailhost_command("jls -q jid name host.hostname path") | |
if code != 0: | |
display.vvv("JLS stdout: %s" % stdout) | |
raise AnsibleError("jls returned non-zero!") | |
lines = stdout.strip().split('\n') | |
found = False | |
for line in lines: | |
if line.strip() == '': | |
break | |
jid, name, hostname, path = line.strip().split() | |
if name == self.jailspec or hostname == self.jailspec: | |
self.jid = jid | |
self.jname = name | |
self.jpath = path | |
found = True | |
break | |
if not found: | |
raise AnsibleError("failed to find a jail with name or hostname of '%s'" % self.jailspec) | |
def get_jail_path(self): | |
self.match_jail() | |
return self.jpath | |
def get_jail_id(self): | |
self.match_jail() | |
return self.jid | |
def get_jail_connector(self): | |
if self.connector is None: | |
code, _, _ = self._jailhost_command("which -s jailme") | |
if code != 0: | |
self.connector = 'jexec' | |
else: | |
self.connector = 'jailme' | |
return self.connector | |
def _strip_sudo(self, executable, cmd): | |
# Get the command without sudo | |
sudoless = cmd.rsplit(executable + ' -c ', 1)[1] | |
# Get the quotes | |
quotes = sudoless.partition('echo')[0] | |
# Get the string between the quotes | |
cmd = sudoless[len(quotes):-len(quotes+'?')] | |
# Drop the first command becasue we don't need it | |
cmd = cmd.split('; ', 1)[1] | |
return cmd | |
def _strip_sleep(self, cmd): | |
# Get the command without sleep | |
cmd = cmd.split(' && sleep 0', 1)[0] | |
# Add back trailing quote | |
cmd = '%s%s' % (cmd, "'") | |
return cmd | |
def _jailhost_command(self, cmd): | |
return super(Connection, self).exec_command(cmd, in_data=None, sudoable=True) | |
def exec_command(self, cmd, in_data=None, executable='/bin/sh', sudoable=True): | |
slpcmd = '' | |
''' run a command in the jail ''' | |
if '&& sleep 0' in cmd: | |
slpcmd = True | |
cmd = self._strip_sleep(cmd) | |
if 'sudo' in cmd: | |
cmd = self._strip_sudo(executable, cmd) | |
cmd = ' '.join([executable, '-c', pipes.quote(cmd)]) | |
if slpcmd == True: | |
cmd = '%s %s %s %s' % (self.get_jail_connector(), self.get_jail_id(), cmd, '&& sleep 0') | |
else: | |
cmd = '%s %s %s' % (self.get_jail_connector(), self.get_jail_id(), cmd) | |
if self._play_context.become: | |
# display.debug("_low_level_execute_command(): using become for this command") | |
cmd = self._play_context.make_become_cmd(cmd) | |
# display.vvv("JAIL (%s) %s" % (local_cmd), host=self.host) | |
return super(Connection, self).exec_command(cmd, in_data, True) | |
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, from_file, to_file): | |
if self._play_context.become: | |
# display.debug("_low_level_execute_command(): using become for this command") | |
copycmd = self._play_context.make_become_cmd(' '.join(['cp', from_file, to_file])) | |
else: | |
copycmd = ' '.join(['cp', from_file, to_file]) | |
display.vvv(u"REMOTE COPY {0} TO {1}".format(from_file, to_file), host=self.inventory_hostname) | |
code, stdout, stderr = self._jailhost_command(copycmd) | |
if code != 0: | |
raise AnsibleError("failed to move file from %s to %s:\n%s\n%s" % (from_file, to_file, stdout, stderr)) | |
@contextmanager | |
def tempfile(self): | |
code, stdout, stderr = self._jailhost_command('mktemp') | |
if code != 0: | |
raise AnsibleError("failed to make temp file:\n%s\n%s" % (stdout, stderr)) | |
tmp = stdout.strip().split('\n')[-1] | |
code, stdout, stderr = self._jailhost_command(' '.join(['chmod 0644', tmp])) | |
if code != 0: | |
raise AnsibleError("failed to make temp file %s world readable:\n%s\n%s" % (tmp, stdout, stderr)) | |
yield tmp | |
code, stdout, stderr = self._jailhost_command(' '.join(['rm', tmp])) | |
if code != 0: | |
raise AnsibleError("failed to remove temp file %s:\n%s\n%s" % (tmp, stdout, stderr)) | |
def put_file(self, in_path, out_path): | |
''' transfer a file from local to remote jail ''' | |
out_path = self._normalize_path(out_path, self.get_jail_path()) | |
with self.tempfile() as tmp: | |
super(Connection, self).put_file(in_path, tmp) | |
self._copy_file(tmp, out_path) | |
def fetch_file(self, in_path, out_path): | |
''' fetch a file from remote to local ''' | |
in_path = self._normalize_path(in_path, self.get_jail_path()) | |
with self.tempfile() as tmp: | |
self._copy_file(in_path, tmp) | |
super(Connection, self).fetch_file(tmp, out_path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment