issue #477: backport ansible_mitogen/target.py to Python2.4
This commit is contained in:
parent
462a8567e5
commit
256628c149
|
@ -31,14 +31,8 @@ Helper functions intended to be executed on the target. These are entrypoints
|
|||
for file transfer, module execution and sundry bits like changing file modes.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import errno
|
||||
import functools
|
||||
import grp
|
||||
import json
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
import pwd
|
||||
|
@ -51,10 +45,32 @@ import tempfile
|
|||
import traceback
|
||||
import types
|
||||
|
||||
# Absolute imports for <2.5.
|
||||
logging = __import__('logging')
|
||||
|
||||
import mitogen.core
|
||||
import mitogen.fork
|
||||
import mitogen.parent
|
||||
import mitogen.service
|
||||
from mitogen.core import b
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
try:
|
||||
reduce
|
||||
except ImportError:
|
||||
# Python 2.4
|
||||
from functools import reduce
|
||||
|
||||
try:
|
||||
BaseException
|
||||
except NameError:
|
||||
# Python 2.4
|
||||
BaseException = Exception
|
||||
|
||||
|
||||
# Ansible since PR #41749 inserts "import __main__" into
|
||||
# ansible.module_utils.basic. Mitogen's importer will refuse such an import, so
|
||||
|
@ -70,14 +86,14 @@ import ansible_mitogen.runner
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAKE_TEMP_FAILED_MSG = (
|
||||
"Unable to find a useable temporary directory. This likely means no\n"
|
||||
"system-supplied TMP directory can be written to, or all directories\n"
|
||||
"were mounted on 'noexec' filesystems.\n"
|
||||
"\n"
|
||||
"The following paths were tried:\n"
|
||||
" %(namelist)s\n"
|
||||
"\n"
|
||||
"Please check '-vvv' output for a log of individual path errors."
|
||||
u"Unable to find a useable temporary directory. This likely means no\n"
|
||||
u"system-supplied TMP directory can be written to, or all directories\n"
|
||||
u"were mounted on 'noexec' filesystems.\n"
|
||||
u"\n"
|
||||
u"The following paths were tried:\n"
|
||||
u" %(namelist)s\n"
|
||||
u"\n"
|
||||
u"Please check '-vvv' output for a log of individual path errors."
|
||||
)
|
||||
|
||||
|
||||
|
@ -99,7 +115,7 @@ def subprocess__Popen__close_fds(self, but):
|
|||
a version that is O(fds) rather than O(_SC_OPEN_MAX).
|
||||
"""
|
||||
try:
|
||||
names = os.listdir('/proc/self/fd')
|
||||
names = os.listdir(u'/proc/self/fd')
|
||||
except OSError:
|
||||
# May fail if acting on a container that does not have /proc mounted.
|
||||
self._original_close_fds(but)
|
||||
|
@ -118,9 +134,9 @@ def subprocess__Popen__close_fds(self, but):
|
|||
|
||||
|
||||
if (
|
||||
sys.platform.startswith('linux') and
|
||||
sys.version < '3.0' and
|
||||
hasattr(subprocess.Popen, '_close_fds') and
|
||||
sys.platform.startswith(u'linux') and
|
||||
sys.version < u'3.0' and
|
||||
hasattr(subprocess.Popen, u'_close_fds') and
|
||||
not mitogen.is_master
|
||||
):
|
||||
subprocess.Popen._original_close_fds = subprocess.Popen._close_fds
|
||||
|
@ -142,7 +158,7 @@ def get_small_file(context, path):
|
|||
Bytestring file data.
|
||||
"""
|
||||
pool = mitogen.service.get_or_create_pool(router=context.router)
|
||||
service = pool.get_service('mitogen.service.PushFileService')
|
||||
service = pool.get_service(u'mitogen.service.PushFileService')
|
||||
return service.get(path)
|
||||
|
||||
|
||||
|
@ -184,9 +200,10 @@ def transfer_file(context, in_path, out_path, sync=False, set_owner=False):
|
|||
if not ok:
|
||||
raise IOError('transfer of %r was interrupted.' % (in_path,))
|
||||
|
||||
os.fchmod(fp.fileno(), metadata['mode'])
|
||||
set_file_mode(tmp_path, metadata['mode'], fd=fp.fileno())
|
||||
if set_owner:
|
||||
set_fd_owner(fp.fileno(), metadata['owner'], metadata['group'])
|
||||
set_file_owner(tmp_path, metadata['owner'], metadata['group'],
|
||||
fd=fp.fileno())
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
|
@ -209,7 +226,8 @@ def prune_tree(path):
|
|||
try:
|
||||
os.unlink(path)
|
||||
return
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
e = sys.exc_info()[1]
|
||||
if not (os.path.isdir(path) and
|
||||
e.args[0] in (errno.EPERM, errno.EISDIR)):
|
||||
LOG.error('prune_tree(%r): %s', path, e)
|
||||
|
@ -219,7 +237,8 @@ def prune_tree(path):
|
|||
# Ensure write access for readonly directories. Ignore error in case
|
||||
# path is on a weird filesystem (e.g. vfat).
|
||||
os.chmod(path, int('0700', 8))
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
e = sys.exc_info()[1]
|
||||
LOG.warning('prune_tree(%r): %s', path, e)
|
||||
|
||||
try:
|
||||
|
@ -227,7 +246,8 @@ def prune_tree(path):
|
|||
if name not in ('.', '..'):
|
||||
prune_tree(os.path.join(path, name))
|
||||
os.rmdir(path)
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
e = sys.exc_info()[1]
|
||||
LOG.error('prune_tree(%r): %s', path, e)
|
||||
|
||||
|
||||
|
@ -248,7 +268,8 @@ def is_good_temp_dir(path):
|
|||
if not os.path.exists(path):
|
||||
try:
|
||||
os.makedirs(path, mode=int('0700', 8))
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
e = sys.exc_info()[1]
|
||||
LOG.debug('temp dir %r unusable: did not exist and attempting '
|
||||
'to create it failed: %s', path, e)
|
||||
return False
|
||||
|
@ -258,14 +279,16 @@ def is_good_temp_dir(path):
|
|||
prefix='ansible_mitogen_is_good_temp_dir',
|
||||
dir=path,
|
||||
)
|
||||
except (OSError, IOError) as e:
|
||||
except (OSError, IOError):
|
||||
e = sys.exc_info()[1]
|
||||
LOG.debug('temp dir %r unusable: %s', path, e)
|
||||
return False
|
||||
|
||||
try:
|
||||
try:
|
||||
os.chmod(tmp.name, int('0700', 8))
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
e = sys.exc_info()[1]
|
||||
LOG.debug('temp dir %r unusable: chmod failed: %s', path, e)
|
||||
return False
|
||||
|
||||
|
@ -273,7 +296,8 @@ def is_good_temp_dir(path):
|
|||
# access(.., X_OK) is sufficient to detect noexec.
|
||||
if not os.access(tmp.name, os.X_OK):
|
||||
raise OSError('filesystem appears to be mounted noexec')
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
e = sys.exc_info()[1]
|
||||
LOG.debug('temp dir %r unusable: %s', path, e)
|
||||
return False
|
||||
finally:
|
||||
|
@ -351,9 +375,9 @@ def init_child(econtext, log_level, candidate_temp_dirs):
|
|||
good_temp_dir = find_good_temp_dir(candidate_temp_dirs)
|
||||
|
||||
return {
|
||||
'fork_context': _fork_parent,
|
||||
'home_dir': mitogen.core.to_text(os.path.expanduser('~')),
|
||||
'good_temp_dir': good_temp_dir,
|
||||
u'fork_context': _fork_parent,
|
||||
u'home_dir': mitogen.core.to_text(os.path.expanduser('~')),
|
||||
u'good_temp_dir': good_temp_dir,
|
||||
}
|
||||
|
||||
|
||||
|
@ -379,7 +403,7 @@ def run_module(kwargs):
|
|||
"""
|
||||
runner_name = kwargs.pop('runner_name')
|
||||
klass = getattr(ansible_mitogen.runner, runner_name)
|
||||
impl = klass(**kwargs)
|
||||
impl = klass(**mitogen.core.Kwargs(kwargs))
|
||||
return impl.run()
|
||||
|
||||
|
||||
|
@ -412,8 +436,11 @@ class AsyncRunner(object):
|
|||
dct.setdefault('ansible_job_id', self.job_id)
|
||||
dct.setdefault('data', '')
|
||||
|
||||
with open(self.path + '.tmp', 'w') as fp:
|
||||
fp = open(self.path + '.tmp', 'w')
|
||||
try:
|
||||
fp.write(json.dumps(dct))
|
||||
finally:
|
||||
fp.close()
|
||||
os.rename(self.path + '.tmp', self.path)
|
||||
|
||||
def _on_sigalrm(self, signum, frame):
|
||||
|
@ -565,8 +592,8 @@ def exec_args(args, in_data='', chdir=None, shell=None, emulate_tty=False):
|
|||
stdout, stderr = proc.communicate(in_data)
|
||||
|
||||
if emulate_tty:
|
||||
stdout = stdout.replace(b'\n', b'\r\n')
|
||||
return proc.returncode, stdout, stderr or b''
|
||||
stdout = stdout.replace(b('\n'), b('\r\n'))
|
||||
return proc.returncode, stdout, stderr or b('')
|
||||
|
||||
|
||||
def exec_command(cmd, in_data='', chdir=None, shell=None, emulate_tty=False):
|
||||
|
@ -598,7 +625,7 @@ def read_path(path):
|
|||
return open(path, 'rb').read()
|
||||
|
||||
|
||||
def set_fd_owner(fd, owner, group=None):
|
||||
def set_file_owner(path, owner, group=None, fd=None):
|
||||
if owner:
|
||||
uid = pwd.getpwnam(owner).pw_uid
|
||||
else:
|
||||
|
@ -609,7 +636,11 @@ def set_fd_owner(fd, owner, group=None):
|
|||
else:
|
||||
gid = os.getegid()
|
||||
|
||||
os.fchown(fd, (uid, gid))
|
||||
if fd is not None and hasattr(os, 'fchown'):
|
||||
os.fchown(fd, (uid, gid))
|
||||
else:
|
||||
# Python<2.6
|
||||
os.chown(path, (uid, gid))
|
||||
|
||||
|
||||
def write_path(path, s, owner=None, group=None, mode=None,
|
||||
|
@ -627,9 +658,9 @@ def write_path(path, s, owner=None, group=None, mode=None,
|
|||
try:
|
||||
try:
|
||||
if mode:
|
||||
os.fchmod(fp.fileno(), mode)
|
||||
set_file_mode(tmp_path, mode, fd=fp.fileno())
|
||||
if owner or group:
|
||||
set_fd_owner(fp.fileno(), owner, group)
|
||||
set_file_owner(tmp_path, owner, group, fd=fp.fileno())
|
||||
fp.write(s)
|
||||
finally:
|
||||
fp.close()
|
||||
|
@ -676,7 +707,7 @@ def apply_mode_spec(spec, mode):
|
|||
mask = CHMOD_MASKS[ch]
|
||||
bits = CHMOD_BITS[ch]
|
||||
cur_perm_bits = mode & mask
|
||||
new_perm_bits = functools.reduce(operator.or_, (bits[p] for p in perms), 0)
|
||||
new_perm_bits = reduce(operator.or_, (bits[p] for p in perms), 0)
|
||||
mode &= ~mask
|
||||
if op == '=':
|
||||
mode |= new_perm_bits
|
||||
|
@ -687,15 +718,19 @@ def apply_mode_spec(spec, mode):
|
|||
return mode
|
||||
|
||||
|
||||
def set_file_mode(path, spec):
|
||||
def set_file_mode(path, spec, fd=None):
|
||||
"""
|
||||
Update the permissions of a file using the same syntax as chmod(1).
|
||||
"""
|
||||
mode = os.stat(path).st_mode
|
||||
|
||||
if spec.isdigit():
|
||||
if isinstance(spec, (int, long)):
|
||||
new_mode = spec
|
||||
elif spec.isdigit():
|
||||
new_mode = int(spec, 8)
|
||||
else:
|
||||
mode = os.stat(path).st_mode
|
||||
new_mode = apply_mode_spec(spec, mode)
|
||||
|
||||
os.chmod(path, new_mode)
|
||||
if fd is not None and hasattr(os, 'fchmod'):
|
||||
os.fchmod(fd, new_mode)
|
||||
else:
|
||||
os.chmod(path, new_mode)
|
||||
|
|
Loading…
Reference in New Issue