Commit 5dcaa304 by Chris Church Committed by Matt Martz

Add shell_plugins to abstract shell-specific functions out of runner, add winrm…

Add shell_plugins to abstract shell-specific functions out of runner, add winrm connection plugin, add initial Windows modules.
parent 627ff30a
......@@ -119,7 +119,7 @@ class ActionModule(object):
# fix file permissions when the copy is done as a different user
if self.runner.sudo and self.runner.sudo_user != 'root':
self.runner._low_level_exec_command(conn, "chmod a+r %s" % xfered, tmp)
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
# run the copy module
module_args = "%s src=%s dest=%s original_basename=%s" % (module_args, pipes.quote(xfered), pipes.quote(dest), pipes.quote(os.path.basename(src)))
......
......@@ -37,7 +37,7 @@ class ActionModule(object):
tmp = self.runner._make_tmp_path(conn)
(module_path, is_new_style, shebang) = self.runner._copy_module(conn, tmp, module_name, module_args, inject, complex_args=complex_args)
self.runner._low_level_exec_command(conn, "chmod a+rx %s" % module_path, tmp)
self.runner._remote_chmod(conn, 'a+rx', module_path, tmp)
return self.runner._execute_module(conn, tmp, 'async_wrapper', module_args,
async_module=module_path,
......
......@@ -169,7 +169,7 @@ class ActionModule(object):
# This is kind of optimization - if user told us destination is
# dir, do path manipulation right away, otherwise we still check
# for dest being a dir via remote call below.
if dest.endswith("/"):
if dest.endswith("/"): # CCTODO: Fixme for powershell
dest_file = os.path.join(dest, source_rel)
else:
dest_file = dest
......@@ -186,7 +186,7 @@ class ActionModule(object):
return ReturnData(conn=conn, result=result)
else:
# Append the relative source location to the destination and retry remote_md5.
dest_file = os.path.join(dest, source_rel)
dest_file = os.path.join(dest, source_rel) # CCTODO
remote_md5 = self.runner._remote_md5(conn, tmp_path, dest_file)
if remote_md5 != '1' and not force:
......@@ -228,7 +228,7 @@ class ActionModule(object):
# fix file permissions when the copy is done as a different user
if self.runner.sudo and self.runner.sudo_user != 'root' and not raw:
self.runner._low_level_exec_command(conn, "chmod a+r %s" % tmp_src, tmp_path)
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
if raw:
# Continue to next iteration if raw is defined.
......
......@@ -59,7 +59,7 @@ class ActionModule(object):
source = os.path.expanduser(source)
if flat:
if dest.endswith("/"):
if dest.endswith("/"): # CCTODO
# if the path ends with "/", we'll use the source filename as the
# destination filename
base = os.path.basename(source)
......
......@@ -106,7 +106,8 @@ class ActionModule(object):
# transfer the file to a remote tmp location
source = source.replace('\x00', '') # why does this happen here?
args = args.replace('\x00', '') # why does this happen here?
tmp_src = os.path.join(tmp, os.path.basename(source))
#tmp_src = os.path.join(tmp, os.path.basename(source)) # CCTODO
tmp_src = conn.shell.join_path(tmp, os.path.basename(source))
tmp_src = tmp_src.replace('\x00', '')
conn.put_file(source, tmp_src)
......@@ -115,14 +116,14 @@ class ActionModule(object):
# set file permissions, more permisive when the copy is done as a different user
if ((self.runner.sudo and self.runner.sudo_user != 'root') or
(self.runner.su and self.runner.su_user != 'root')):
cmd_args_chmod = "chmod a+rx %s" % tmp_src
chmod_mode = 'a+rx'
sudoable = False
else:
cmd_args_chmod = "chmod +rx %s" % tmp_src
self.runner._low_level_exec_command(conn, cmd_args_chmod, tmp, sudoable=sudoable, su=self.runner.su)
chmod_mode = '+rx'
self.runner._remote_chmod(conn, chmod_mode, tmp_src, tmp, sudoable=sudoable, su=self.runner.su)
# add preparation steps to one ssh roundtrip executing the script
env_string = self.runner._compute_environment_string(inject)
env_string = self.runner._compute_environment_string(conn, inject)
module_args = env_string + tmp_src + ' ' + args
handler = utils.plugins.action_loader.get('raw', self.runner)
......@@ -130,7 +131,7 @@ class ActionModule(object):
# clean up after
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES:
self.runner._low_level_exec_command(conn, 'rm -rf %s >/dev/null 2>&1' % tmp, tmp)
self.runner._remove_tmp_path(conn, tmp)
result.result['changed'] = True
......
......@@ -79,7 +79,7 @@ class ActionModule(object):
source = utils.path_dwim(self.runner.basedir, source)
if dest.endswith("/"):
if dest.endswith("/"): # CCTODO
base = os.path.basename(source)
dest = os.path.join(dest, base)
......@@ -114,7 +114,7 @@ class ActionModule(object):
# fix file permissions when the copy is done as a different user
if self.runner.sudo and self.runner.sudo_user != 'root':
self.runner._low_level_exec_command(conn, "chmod a+r %s" % xfered, tmp)
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
# run the copy module
module_args = "%s src=%s dest=%s original_basename=%s" % (module_args, pipes.quote(xfered), pipes.quote(dest), pipes.quote(os.path.basename(source)))
......
......@@ -77,7 +77,7 @@ class ActionModule(object):
# fix file permissions when the copy is done as a different user
if copy:
if self.runner.sudo and self.runner.sudo_user != 'root':
self.runner._low_level_exec_command(conn, "chmod a+r %s" % tmp_src, tmp)
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
module_args = "%s src=%s original_basename=%s" % (module_args, pipes.quote(tmp_src), pipes.quote(os.path.basename(source)))
else:
module_args = "%s original_basename=%s" % (module_args, pipes.quote(os.path.basename(source)))
......
......@@ -25,18 +25,15 @@ import ansible.constants as C
import os
import os.path
class Connection(object):
class Connector(object):
''' Handles abstract connections to remote hosts '''
def __init__(self, runner):
self.runner = runner
def connect(self, host, port, user, password, transport, private_key_file):
conn = None
conn = utils.plugins.connection_loader.get(transport, self.runner, host, port, user=user, password=password, private_key_file=private_key_file)
if conn is None:
raise AnsibleError("unsupported connection type: %s" % transport)
self.active = conn.connect()
return self.active
from ansible.runner.shell_plugins.sh import ShellModule as ShModule
class ShellModule(ShModule):
def env_prefix(self, **kwargs):
return 'env %s' % super(ShellModule, self).env_prefix(**kwargs)
from ansible.runner.shell_plugins.sh import ShellModule as ShModule
class ShellModule(ShModule):
def env_prefix(self, **kwargs):
return 'env %s' % super(ShellModule, self).env_prefix(**kwargs)
import base64
import os
import re
import random
import shlex
import time
class ShellModule(object):
def __init__(self):
pass
def _escape(self, value, include_vars=False):
'''
Return value escaped for use in PowerShell command.
'''
# http://www.techotopia.com/index.php/Windows_PowerShell_1.0_String_Quoting_and_Escape_Sequences
# http://stackoverflow.com/questions/764360/a-list-of-string-replacements-in-python
subs = [('\n', '`n'), ('\r', '`r'), ('\t', '`t'), ('\a', '`a'),
('\b', '`b'), ('\f', '`f'), ('\v', '`v'), ('"', '`"'),
('\'', '`\''), ('`', '``'), ('\x00', '`0')]
if include_vars:
subs.append(('$', '`$'))
pattern = '|'.join('(%s)' % re.escape(p) for p, s in subs)
substs = [s for p, s in subs]
replace = lambda m: substs[m.lastindex - 1]
return re.sub(pattern, replace, value)
def _get_script_cmd(self, script):
'''
Convert a PowerShell script to a single base64-encoded command.
'''
encoded_script = base64.b64encode(script.encode('utf-16-le'))
return ' '.join(['PowerShell', '-NoProfile', '-NonInteractive',
'-EncodedCommand', encoded_script])
def env_prefix(self, **kwargs):
return ''
def join_path(self, *args):
return os.path.join(*args).replace('/', '\\')
def chmod(self, mode, path):
return ''
def remove(self, path, recurse=False):
path = self._escape(path)
if recurse:
return self._get_script_cmd('''Remove-Item "%s" -Force -Recurse;''' % path)
else:
return self._get_script_cmd('''Remove-Item "%s" -Force;''' % path)
def mkdtemp(self, basefile=None, system=False, mode=None):
if not basefile:
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
basefile = self._escape(basefile)
# FIXME: Support system temp path!
return self._get_script_cmd('''(New-Item -Type Directory -Path $env:temp -Name "%s").FullName;''' % basefile)
def md5(self, path):
path = self._escape(path)
return self._get_script_cmd('''(Get-FileHash -Path "%s" -Algorithm MD5).Hash.ToLower();''' % path)
def build_module_command(self, env_string, shebang, cmd, rm_tmp=None):
cmd_parts = shlex.split(cmd, posix=False)
if not cmd_parts[0].lower().endswith('.ps1'):
cmd_parts[0] = '%s.ps1' % cmd_parts[0]
cmd_parts = ['PowerShell', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Unrestricted', '-File'] + ['"%s"' % x for x in cmd_parts]
script = ' '.join(cmd_parts)
if rm_tmp:
script = '%s; Remove-Item "%s" -Force -Recurse;' % (script, self._escape(rm_tmp))
return self._get_script_cmd(script)
import os
import pipes
import ansible.constants as C
class ShellModule(object):
def __init__(self):
pass
def env_prefix(self, **kwargs):
'''Build command prefix with environment variables.'''
env = dict(
LANG = C.DEFAULT_MODULE_LANG,
LC_CTYPE = C.DEFAULT_MODULE_LANG,
)
env.update(kwargs)
return ' '.join(['%s=%s' % (k, pipes.quote(unicode(v))) for k,v in env.items()])
def join_path(self, *args):
return os.path.join(*args)
def chmod(self, mode, path):
path = pipes.quote(path)
return 'chmod %s %s' % (mode, path)
def remove(self, path, recurse=False):
path = pipes.quote(path)
if recurse:
return "rm -rf %s >/dev/null 2>&1" % path
else:
return "rm -f %s >/dev/null 2>&1" % path
def mkdtemp(self, basefile=None, system=False, mode=None):
if not basefile:
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
basetmp = self.join_path(C.DEFAULT_REMOTE_TMP, basefile)
if system and basetmp.startswith('$HOME'):
basetmp = self.join_path('/tmp', basefile)
cmd = 'mkdir -p %s' % basetmp
if mode:
cmd += ' && chmod %s %s' % (mode, basetmp)
cmd += ' && echo %s' % basetmp
return cmd
def md5(self, path):
path = pipes.quote(path)
# The following test needs to be SH-compliant. BASH-isms will
# not work if /bin/sh points to a non-BASH shell.
test = "rc=0; [ -r \"%s\" ] || rc=2; [ -f \"%s\" ] || rc=1; [ -d \"%s\" ] && echo 3 && exit 0" % ((path,) * 3)
md5s = [
"(/usr/bin/md5sum %s 2>/dev/null)" % path, # Linux
"(/sbin/md5sum -q %s 2>/dev/null)" % path, # ?
"(/usr/bin/digest -a md5 %s 2>/dev/null)" % path, # Solaris 10+
"(/sbin/md5 -q %s 2>/dev/null)" % path, # Freebsd
"(/usr/bin/md5 -n %s 2>/dev/null)" % path, # Netbsd
"(/bin/md5 -q %s 2>/dev/null)" % path, # Openbsd
"(/usr/bin/csum -h MD5 %s 2>/dev/null)" % path, # AIX
"(/bin/csum -h MD5 %s 2>/dev/null)" % path # AIX also
]
cmd = " || ".join(md5s)
cmd = "%s; %s || (echo \"${rc} %s\")" % (test, cmd, path)
return cmd
def build_module_command(self, env_string, shebang, cmd, rm_tmp=None):
cmd_parts = [env_string.strip(), shebang.replace("#!", "").strip(), cmd]
new_cmd = " ".join(cmd_parts)
if rm_tmp:
new_cmd = '%s; rm -rf %s >/dev/null 2>&1' % (new_cmd, rm_tmp)
return new_cmd
......@@ -139,21 +139,25 @@ class PluginLoader(object):
if directory not in self._extra_dirs:
self._extra_dirs.append(directory)
def find_plugin(self, name):
def find_plugin(self, name, suffixes=None):
''' Find a plugin named name '''
if name in self._plugin_path_cache:
return self._plugin_path_cache[name]
if not suffixes:
if self.class_name:
suffixes = ['.py']
else:
suffixes = ['']
suffix = ".py"
if not self.class_name:
suffix = ""
for suffix in suffixes:
full_name = '%s%s' % (name, suffix)
if full_name in self._plugin_path_cache:
return self._plugin_path_cache[full_name]
for i in self._get_paths():
path = os.path.join(i, "%s%s" % (name, suffix))
if os.path.isfile(path):
self._plugin_path_cache[name] = path
return path
for i in self._get_paths():
path = os.path.join(i, full_name)
if os.path.isfile(path):
self._plugin_path_cache[full_name] = path
return path
return None
......@@ -212,6 +216,13 @@ connection_loader = PluginLoader(
aliases={'paramiko': 'paramiko_ssh'}
)
shell_loader = PluginLoader(
'ShellModule',
'ansible.runner.shell_plugins',
'shell_plugins',
'shell_plugins',
)
module_finder = PluginLoader(
'',
'',
......
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'FIXME';
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name fixme -Value $data;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'FIXME';
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name fixme -Value $data;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'FIXME';
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name fixme -Value $data;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'FIXME';
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name fixme -Value $data;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'FIXME';
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name fixme -Value $data;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'pong';
If (($params | Get-Member | Select-Object -ExpandProperty Name) -contains 'data')
{
$data = $params.data;
}
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name ping -Value $data;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
$params = '{}' | ConvertFrom-Json;
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$src = '';
If (($params | Get-Member | Select-Object -ExpandProperty Name) -contains 'src')
{
$src = $params.src;
}
Else
{
If (($params | Get-Member | Select-Object -ExpandProperty Name) -contains 'path')
{
$src = $params.path;
}
}
If (-not $src)
{
}
$bytes = [System.IO.File]::ReadAllBytes($src);
$content = [System.Convert]::ToBase64String($bytes);
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name content -Value $content;
$result | Add-Member -MemberType NoteProperty -Name encoding -Value 'base64';
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
$params = '{}' | ConvertFrom-Json;
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$path = '';
If (($params | Get-Member | Select-Object -ExpandProperty Name) -contains 'path')
{
$path = $params.path;
}
$get_md5 = $TRUE;
If (($params | Get-Member | Select-Object -ExpandProperty Name) -contains 'get_md5')
{
$get_md5 = $params.get_md5;
}
$stat = '{}' | ConvertFrom-Json;
If (Test-Path $path)
{
$stat | Add-Member -MemberType NoteProperty -Name exists -Value $TRUE;
$info = Get-Item $path;
If ($info.Directory) # Only files have the .Directory attribute.
{
$stat | Add-Member -MemberType NoteProperty -Name isdir -Value $FALSE;
$stat | Add-Member -MemberType NoteProperty -Name size -Value $info.Length;
}
Else
{
$stat | Add-Member -MemberType NoteProperty -Name isdir -Value $TRUE;
}
}
Else
{
$stat | Add-Member -MemberType NoteProperty -Name exists -Value $FALSE;
}
If ($get_md5 -and $stat.exists -and -not $stat.isdir)
{
$path_md5 = (Get-FileHash -Path $path -Algorithm MD5).Hash.ToLower();
$stat | Add-Member -MemberType NoteProperty -Name md5 -Value $path_md5;
}
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name stat -Value $stat;
$result | Add-Member -MemberType NoteProperty -Name changed -Value $FALSE;
echo $result | ConvertTo-Json;
#!powershell
# WANT_JSON
If ($args.Length -gt 0)
{
$params = Get-Content $args[0] | ConvertFrom-Json;
}
$data = 'pong';
If (($params | Get-Member | Select-Object -ExpandProperty Name) -contains 'data')
{
$data = $params.data;
}
$result = '{}' | ConvertFrom-Json;
$result | Add-Member -MemberType NoteProperty -Name ping -Value $data;
echo $result | ConvertTo-Json;
# _______ _ _
# |__ __| | (_)
# | | | |__ _ ___
# | | | '_ \| / __|
# | | | | | | \__ \
# __|_| |_| |_|_|___/
# |_ _|
# | | ___
# | | / __|
# _| |_\__ \
# |___/\|___/
# / \
# / /\ \
# / ____ \
# /_/ \_\
# | |
# | | __ _ _ __ __ _ ___
# | | / _` | '__/ _` |/ _ \
# | |___| (_| | | | (_| | __/
# |______\__,_|_| \__, |\___|
# __/ |
# ____ _ |___/
# | _ \| | | |
# | |_) | | ___ ___| | __
# | _ <| |/ _ \ / __| |/ /
# | |_) | | (_) | (__| <
# |____/|_|\___/ \___|_|\_\
# / __ \ / _|
# | | | | |_
# | | | | _|
# | |__| | |
# \____/|_| __ __
# / ____| | / _|/ _|
# | (___ | |_ _ _| |_| |_
# \___ \| __| | | | _| _|
# ____) | |_| |_| | | | |
# |_____/ \__|\__,_|_| |_|
# | | | |
# | |_ _ ___| |_
# _ | | | | / __| __|
# | |__| | |_| \__ \ |_
# \____/_\__,_|___/\__|
# |__ __|
# | | ___
# | |/ _ \
# | | (_) |
# __|_|\___/ _
# | \/ | | |
# | \ / | __ _| | _____
# | |\/| |/ _` | |/ / _ \
# | | | | (_| | < __/
# |_|__|_|\__,_|_|\_\___|
# |__ __| |
# | | | |__ ___
# | | | '_ \ / _ \
# | | | | | | __/
# __|_|_ |_| |_|\___|
# | ____(_) |
# | |__ _| | ___
# | __| | | |/ _ \
# | | | | | __/
# |_|__ |_|_|\___|
# | _ \(_)
# | |_) |_ __ _ __ _ ___ _ __
# | _ <| |/ _` |/ _` |/ _ \ '__|
# | |_) | | (_| | (_| | __/ |
# |____/|_|\__, |\__, |\___|_|
# __/ | __/ |
# |___/ |___/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment