mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-27 10:40:22 -07:00
Fixing winrm connection for v2
This commit is contained in:
parent
946c37fd88
commit
3aede800c5
7 changed files with 125 additions and 125 deletions
|
@ -48,7 +48,7 @@ class ConnectionInformation:
|
||||||
self.remote_addr = None
|
self.remote_addr = None
|
||||||
self.remote_user = None
|
self.remote_user = None
|
||||||
self.password = passwords.get('conn_pass','')
|
self.password = passwords.get('conn_pass','')
|
||||||
self.port = 22
|
self.port = None
|
||||||
self.private_key_file = C.DEFAULT_PRIVATE_KEY_FILE
|
self.private_key_file = C.DEFAULT_PRIVATE_KEY_FILE
|
||||||
self.timeout = C.DEFAULT_TIMEOUT
|
self.timeout = C.DEFAULT_TIMEOUT
|
||||||
|
|
||||||
|
|
|
@ -56,22 +56,12 @@ class ActionBase:
|
||||||
|
|
||||||
def get_shell(self):
|
def get_shell(self):
|
||||||
|
|
||||||
# FIXME: no more inject, get this from the host variables?
|
if hasattr(self._connection, '_shell'):
|
||||||
#default_shell = getattr(self._connection, 'default_shell', '')
|
shell_plugin = getattr(self._connection, '_shell', '')
|
||||||
#shell_type = inject.get('ansible_shell_type')
|
else:
|
||||||
#if not shell_type:
|
shell_plugin = shell_loader.get(os.path.basename(C.DEFAULT_EXECUTABLE))
|
||||||
# if default_shell:
|
if shell_plugin is None:
|
||||||
# shell_type = default_shell
|
shell_plugin = shell_loader.get('sh')
|
||||||
# else:
|
|
||||||
# shell_type = os.path.basename(C.DEFAULT_EXECUTABLE)
|
|
||||||
|
|
||||||
shell_type = getattr(self._connection, 'default_shell', '')
|
|
||||||
if not shell_type:
|
|
||||||
shell_type = os.path.basename(C.DEFAULT_EXECUTABLE)
|
|
||||||
|
|
||||||
shell_plugin = shell_loader.get(shell_type)
|
|
||||||
if shell_plugin is None:
|
|
||||||
shell_plugin = shell_loader.get('sh')
|
|
||||||
|
|
||||||
return shell_plugin
|
return shell_plugin
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,8 @@ class Connection(ConnectionBase):
|
||||||
if not HAVE_PARAMIKO:
|
if not HAVE_PARAMIKO:
|
||||||
raise AnsibleError("paramiko is not installed")
|
raise AnsibleError("paramiko is not installed")
|
||||||
|
|
||||||
self._display.vvv("ESTABLISH CONNECTION FOR USER: %s on PORT %s TO %s" % (self._connection_info.remote_user, self._connection_info.port, self._connection_info.remote_addr), host=self._connection_info.remote_addr)
|
port = self._connection_info.port or 22
|
||||||
|
self._display.vvv("ESTABLISH CONNECTION FOR USER: %s on PORT %s TO %s" % (self._connection_info.remote_user, port, self._connection_info.remote_addr), host=self._connection_info.remote_addr)
|
||||||
|
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
|
|
||||||
|
@ -170,7 +171,7 @@ class Connection(ConnectionBase):
|
||||||
key_filename=key_filename,
|
key_filename=key_filename,
|
||||||
password=self._connection_info.password,
|
password=self._connection_info.password,
|
||||||
timeout=self._connection_info.timeout,
|
timeout=self._connection_info.timeout,
|
||||||
port=self._connection_info.port
|
port=port,
|
||||||
)
|
)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
msg = str(e)
|
msg = str(e)
|
||||||
|
@ -178,7 +179,7 @@ class Connection(ConnectionBase):
|
||||||
raise AnsibleError("paramiko version issue, please upgrade paramiko on the machine running ansible")
|
raise AnsibleError("paramiko version issue, please upgrade paramiko on the machine running ansible")
|
||||||
elif "Private key file is encrypted" in msg:
|
elif "Private key file is encrypted" in msg:
|
||||||
msg = 'ssh %s@%s:%s : %s\nTo connect as a different user, use -u <username>.' % (
|
msg = 'ssh %s@%s:%s : %s\nTo connect as a different user, use -u <username>.' % (
|
||||||
self._connection_info.remote_user, self._connection_info.remote_addr, self._connection_info.port, msg)
|
self._connection_info.remote_user, self._connection_info.remote_addr, port, msg)
|
||||||
raise AnsibleConnectionFailure(msg)
|
raise AnsibleConnectionFailure(msg)
|
||||||
else:
|
else:
|
||||||
raise AnsibleConnectionFailure(msg)
|
raise AnsibleConnectionFailure(msg)
|
||||||
|
|
|
@ -39,7 +39,7 @@ from ansible.plugins.connections import ConnectionBase
|
||||||
class Connection(ConnectionBase):
|
class Connection(ConnectionBase):
|
||||||
''' ssh based connections '''
|
''' ssh based connections '''
|
||||||
|
|
||||||
def __init__(self, connection_info, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# SSH connection specific init stuff
|
# SSH connection specific init stuff
|
||||||
self.HASHED_KEY_MAGIC = "|1|"
|
self.HASHED_KEY_MAGIC = "|1|"
|
||||||
self._has_pipelining = True
|
self._has_pipelining = True
|
||||||
|
@ -50,7 +50,7 @@ class Connection(ConnectionBase):
|
||||||
self._cp_dir = '/tmp'
|
self._cp_dir = '/tmp'
|
||||||
#fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
|
#fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
|
||||||
|
|
||||||
super(Connection, self).__init__(connection_info, *args, **kwargs)
|
super(Connection, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def transport(self):
|
def transport(self):
|
||||||
|
|
|
@ -23,17 +23,13 @@ import re
|
||||||
import shlex
|
import shlex
|
||||||
import traceback
|
import traceback
|
||||||
import urlparse
|
import urlparse
|
||||||
from ansible import errors
|
|
||||||
from ansible import utils
|
|
||||||
from ansible.callbacks import vvv, vvvv, verbose
|
|
||||||
from ansible.runner.shell_plugins import powershell
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from winrm import Response
|
from winrm import Response
|
||||||
from winrm.exceptions import WinRMTransportError
|
from winrm.exceptions import WinRMTransportError
|
||||||
from winrm.protocol import Protocol
|
from winrm.protocol import Protocol
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise errors.AnsibleError("winrm is not installed")
|
raise AnsibleError("winrm is not installed")
|
||||||
|
|
||||||
HAVE_KERBEROS = False
|
HAVE_KERBEROS = False
|
||||||
try:
|
try:
|
||||||
|
@ -42,10 +38,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def vvvvv(msg, host=None):
|
from ansible import constants as C
|
||||||
verbose(msg, host=host, caplevel=4)
|
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
|
||||||
|
from ansible.plugins.connections import ConnectionBase
|
||||||
|
from ansible.plugins import shell_loader
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(ConnectionBase):
|
||||||
'''WinRM connections over HTTP/HTTPS.'''
|
'''WinRM connections over HTTP/HTTPS.'''
|
||||||
|
|
||||||
transport_schemes = {
|
transport_schemes = {
|
||||||
|
@ -53,69 +51,79 @@ class Connection(object):
|
||||||
'https': [('kerberos', 'https'), ('plaintext', 'https')],
|
'https': [('kerberos', 'https'), ('plaintext', 'https')],
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, runner, host, port, user, password, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.runner = runner
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.user = user
|
|
||||||
self.password = password
|
|
||||||
self.has_pipelining = False
|
|
||||||
self.default_shell = 'powershell'
|
|
||||||
self.default_suffixes = ['.ps1', '']
|
|
||||||
self.protocol = None
|
|
||||||
self.shell_id = None
|
|
||||||
self.delegate = None
|
|
||||||
|
|
||||||
# Add runas support
|
self.has_pipelining = False
|
||||||
#self.become_methods_supported=['runas']
|
self.default_suffixes = ['.ps1', '']
|
||||||
|
self.protocol = None
|
||||||
|
self.shell_id = None
|
||||||
|
self.delegate = None
|
||||||
|
|
||||||
|
self._shell = shell_loader.get('powershell')
|
||||||
|
|
||||||
|
# TODO: Add runas support
|
||||||
self.become_methods_supported=[]
|
self.become_methods_supported=[]
|
||||||
|
|
||||||
|
super(Connection, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def transport(self):
|
||||||
|
''' used to identify this connection object from other classes '''
|
||||||
|
return 'winrm'
|
||||||
|
|
||||||
def _winrm_connect(self):
|
def _winrm_connect(self):
|
||||||
'''
|
'''
|
||||||
Establish a WinRM connection over HTTP/HTTPS.
|
Establish a WinRM connection over HTTP/HTTPS.
|
||||||
'''
|
'''
|
||||||
port = self.port or 5986
|
port = self._connection_info.port or 5986
|
||||||
vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \
|
self._display.vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \
|
||||||
(self.user, port, self.host), host=self.host)
|
(self._connection_info.remote_user, port, self._connection_info.remote_addr), host=self._connection_info.remote_addr)
|
||||||
netloc = '%s:%d' % (self.host, port)
|
netloc = '%s:%d' % (self._connection_info.remote_addr, port)
|
||||||
exc = None
|
exc = None
|
||||||
for transport, scheme in self.transport_schemes['http' if port == 5985 else 'https']:
|
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):
|
if transport == 'kerberos' and (not HAVE_KERBEROS or not '@' in self._connection_info.remote_user):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if transport == 'kerberos':
|
if transport == 'kerberos':
|
||||||
realm = self.user.split('@', 1)[1].strip() or None
|
realm = self._connection_info.remote_user.split('@', 1)[1].strip() or None
|
||||||
else:
|
else:
|
||||||
realm = None
|
realm = None
|
||||||
|
|
||||||
endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', ''))
|
endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', ''))
|
||||||
vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint),
|
|
||||||
host=self.host)
|
self._display.vvvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self._connection_info.remote_addr)
|
||||||
protocol = Protocol(endpoint, transport=transport,
|
protocol = Protocol(
|
||||||
username=self.user, password=self.password,
|
endpoint,
|
||||||
realm=realm)
|
transport=transport,
|
||||||
|
username=self._connection_info.remote_user,
|
||||||
|
password=self._connection_info.password,
|
||||||
|
realm=realm
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
protocol.send_message('')
|
protocol.send_message('')
|
||||||
return protocol
|
return protocol
|
||||||
except WinRMTransportError, exc:
|
except WinRMTransportError, exc:
|
||||||
err_msg = str(exc)
|
err_msg = str(exc)
|
||||||
if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I):
|
if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I):
|
||||||
raise errors.AnsibleError("the connection attempt timed out")
|
raise AnsibleError("the connection attempt timed out")
|
||||||
m = re.search(r'Code\s+?(\d{3})', err_msg)
|
m = re.search(r'Code\s+?(\d{3})', err_msg)
|
||||||
if m:
|
if m:
|
||||||
code = int(m.groups()[0])
|
code = int(m.groups()[0])
|
||||||
if code == 401:
|
if code == 401:
|
||||||
raise errors.AnsibleError("the username/password specified for this server was incorrect")
|
raise AnsibleError("the username/password specified for this server was incorrect")
|
||||||
elif code == 411:
|
elif code == 411:
|
||||||
return protocol
|
return protocol
|
||||||
vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host)
|
self._display.vvvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self._connection_info.remote_addr)
|
||||||
continue
|
continue
|
||||||
if exc:
|
if exc:
|
||||||
raise errors.AnsibleError(str(exc))
|
raise AnsibleError(str(exc))
|
||||||
|
|
||||||
def _winrm_exec(self, command, args=(), from_exec=False):
|
def _winrm_exec(self, command, args=(), from_exec=False):
|
||||||
if from_exec:
|
if from_exec:
|
||||||
vvvv("WINRM EXEC %r %r" % (command, args), host=self.host)
|
self._display.vvvvv("WINRM EXEC %r %r" % (command, args), host=self._connection_info.remote_addr)
|
||||||
else:
|
else:
|
||||||
vvvvv("WINRM EXEC %r %r" % (command, args), host=self.host)
|
self._display.vvvvvv("WINRM EXEC %r %r" % (command, args), host=self._connection_info.remote_addr)
|
||||||
if not self.protocol:
|
if not self.protocol:
|
||||||
self.protocol = self._winrm_connect()
|
self.protocol = self._winrm_connect()
|
||||||
if not self.shell_id:
|
if not self.shell_id:
|
||||||
|
@ -125,49 +133,46 @@ class Connection(object):
|
||||||
command_id = self.protocol.run_command(self.shell_id, command, args)
|
command_id = self.protocol.run_command(self.shell_id, command, args)
|
||||||
response = Response(self.protocol.get_command_output(self.shell_id, command_id))
|
response = Response(self.protocol.get_command_output(self.shell_id, command_id))
|
||||||
if from_exec:
|
if from_exec:
|
||||||
vvvv('WINRM RESULT %r' % response, host=self.host)
|
self._display.vvvvv('WINRM RESULT %r' % response, host=self._connection_info.remote_addr)
|
||||||
else:
|
else:
|
||||||
vvvvv('WINRM RESULT %r' % response, host=self.host)
|
self._display.vvvvvv('WINRM RESULT %r' % response, host=self._connection_info.remote_addr)
|
||||||
vvvvv('WINRM STDOUT %s' % response.std_out, host=self.host)
|
self._display.vvvvvv('WINRM STDOUT %s' % response.std_out, host=self._connection_info.remote_addr)
|
||||||
vvvvv('WINRM STDERR %s' % response.std_err, host=self.host)
|
self._display.vvvvvv('WINRM STDERR %s' % response.std_err, host=self._connection_info.remote_addr)
|
||||||
return response
|
return response
|
||||||
finally:
|
finally:
|
||||||
if command_id:
|
if command_id:
|
||||||
self.protocol.cleanup_command(self.shell_id, command_id)
|
self.protocol.cleanup_command(self.shell_id, command_id)
|
||||||
|
|
||||||
def connect(self):
|
def _connect(self):
|
||||||
if not self.protocol:
|
if not self.protocol:
|
||||||
self.protocol = self._winrm_connect()
|
self.protocol = self._winrm_connect()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None):
|
def exec_command(self, cmd, tmp_path, executable='/bin/sh', 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 = cmd.encode('utf-8')
|
||||||
cmd_parts = shlex.split(cmd, posix=False)
|
cmd_parts = shlex.split(cmd, posix=False)
|
||||||
if '-EncodedCommand' in cmd_parts:
|
if '-EncodedCommand' in cmd_parts:
|
||||||
encoded_cmd = cmd_parts[cmd_parts.index('-EncodedCommand') + 1]
|
encoded_cmd = cmd_parts[cmd_parts.index('-EncodedCommand') + 1]
|
||||||
decoded_cmd = base64.b64decode(encoded_cmd)
|
decoded_cmd = base64.b64decode(encoded_cmd)
|
||||||
vvv("EXEC %s" % decoded_cmd, host=self.host)
|
self._display.vvv("EXEC %s" % decoded_cmd, host=self._connection_info.remote_addr)
|
||||||
else:
|
else:
|
||||||
vvv("EXEC %s" % cmd, host=self.host)
|
self._display.vvv("EXEC %s" % cmd, host=self._connection_info.remote_addr)
|
||||||
# For script/raw support.
|
# For script/raw support.
|
||||||
if cmd_parts and cmd_parts[0].lower().endswith('.ps1'):
|
if cmd_parts and cmd_parts[0].lower().endswith('.ps1'):
|
||||||
script = powershell._build_file_cmd(cmd_parts, quote_args=False)
|
script = self._shell._build_file_cmd(cmd_parts, quote_args=False)
|
||||||
cmd_parts = powershell._encode_script(script, as_list=True)
|
cmd_parts = self._shell._encode_script(script, as_list=True)
|
||||||
try:
|
try:
|
||||||
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True)
|
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise errors.AnsibleError("failed to exec cmd %s" % cmd)
|
raise AnsibleError("failed to exec cmd %s" % cmd)
|
||||||
return (result.status_code, '', result.std_out.encode('utf-8'), result.std_err.encode('utf-8'))
|
return (result.status_code, '', result.std_out.encode('utf-8'), result.std_err.encode('utf-8'))
|
||||||
|
|
||||||
def put_file(self, in_path, out_path):
|
def put_file(self, in_path, out_path):
|
||||||
vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
|
self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._connection_info.remote_addr)
|
||||||
if not os.path.exists(in_path):
|
if not os.path.exists(in_path):
|
||||||
raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path)
|
raise AnsibleFileNotFound("file or module does not exist: %s" % in_path)
|
||||||
with open(in_path) as in_file:
|
with open(in_path) as in_file:
|
||||||
in_size = os.path.getsize(in_path)
|
in_size = os.path.getsize(in_path)
|
||||||
script_template = '''
|
script_template = '''
|
||||||
|
@ -179,8 +184,8 @@ class Connection(object):
|
||||||
[void]$s.Close();
|
[void]$s.Close();
|
||||||
'''
|
'''
|
||||||
# Determine max size of data we can pass per command.
|
# Determine max size of data we can pass per command.
|
||||||
script = script_template % (powershell._escape(out_path), in_size, '', in_size)
|
script = script_template % (self._shell._escape(out_path), in_size, '', in_size)
|
||||||
cmd = powershell._encode_script(script)
|
cmd = self._shell._encode_script(script)
|
||||||
# Encode script with no data, subtract its length from 8190 (max
|
# Encode script with no data, subtract its length from 8190 (max
|
||||||
# windows command length), divide by 2.67 (UTF16LE base64 command
|
# windows command length), divide by 2.67 (UTF16LE base64 command
|
||||||
# encoding), then by 1.35 again (data base64 encoding).
|
# encoding), then by 1.35 again (data base64 encoding).
|
||||||
|
@ -192,19 +197,19 @@ class Connection(object):
|
||||||
if out_data.lower().startswith('#!powershell') and not out_path.lower().endswith('.ps1'):
|
if out_data.lower().startswith('#!powershell') and not out_path.lower().endswith('.ps1'):
|
||||||
out_path = out_path + '.ps1'
|
out_path = out_path + '.ps1'
|
||||||
b64_data = base64.b64encode(out_data)
|
b64_data = base64.b64encode(out_data)
|
||||||
script = script_template % (powershell._escape(out_path), offset, b64_data, in_size)
|
script = script_template % (self._shell._escape(out_path), offset, b64_data, in_size)
|
||||||
vvvv("WINRM PUT %s to %s (offset=%d size=%d)" % (in_path, out_path, offset, len(out_data)), host=self.host)
|
self._display.vvvvv("WINRM PUT %s to %s (offset=%d size=%d)" % (in_path, out_path, offset, len(out_data)), host=self._connection_info.remote_addr)
|
||||||
cmd_parts = powershell._encode_script(script, as_list=True)
|
cmd_parts = self._shell._encode_script(script, as_list=True)
|
||||||
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:])
|
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:])
|
||||||
if result.status_code != 0:
|
if result.status_code != 0:
|
||||||
raise IOError(result.std_err.encode('utf-8'))
|
raise IOError(result.std_err.encode('utf-8'))
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
|
raise AnsibleError("failed to transfer file to %s" % out_path)
|
||||||
|
|
||||||
def fetch_file(self, in_path, out_path):
|
def fetch_file(self, in_path, out_path):
|
||||||
out_path = out_path.replace('\\', '/')
|
out_path = out_path.replace('\\', '/')
|
||||||
vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
|
self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._connection_info.remote_addr)
|
||||||
buffer_size = 2**19 # 0.5MB chunks
|
buffer_size = 2**19 # 0.5MB chunks
|
||||||
if not os.path.exists(os.path.dirname(out_path)):
|
if not os.path.exists(os.path.dirname(out_path)):
|
||||||
os.makedirs(os.path.dirname(out_path))
|
os.makedirs(os.path.dirname(out_path))
|
||||||
|
@ -233,9 +238,9 @@ class Connection(object):
|
||||||
Write-Error "%(path)s does not exist";
|
Write-Error "%(path)s does not exist";
|
||||||
Exit 1;
|
Exit 1;
|
||||||
}
|
}
|
||||||
''' % dict(buffer_size=buffer_size, path=powershell._escape(in_path), offset=offset)
|
''' % dict(buffer_size=buffer_size, path=self._shell._escape(in_path), offset=offset)
|
||||||
vvvv("WINRM FETCH %s to %s (offset=%d)" % (in_path, out_path, offset), host=self.host)
|
self._display.vvvvv("WINRM FETCH %s to %s (offset=%d)" % (in_path, out_path, offset), host=self._connection_info.remote_addr)
|
||||||
cmd_parts = powershell._encode_script(script, as_list=True)
|
cmd_parts = self._shell._encode_script(script, as_list=True)
|
||||||
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:])
|
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:])
|
||||||
if result.status_code != 0:
|
if result.status_code != 0:
|
||||||
raise IOError(result.std_err.encode('utf-8'))
|
raise IOError(result.std_err.encode('utf-8'))
|
||||||
|
@ -259,7 +264,7 @@ class Connection(object):
|
||||||
offset += len(data)
|
offset += len(data)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
|
raise AnsibleError("failed to transfer file to %s" % out_path)
|
||||||
finally:
|
finally:
|
||||||
if out_file:
|
if out_file:
|
||||||
out_file.close()
|
out_file.close()
|
||||||
|
|
|
@ -32,33 +32,6 @@ _powershell_version = os.environ.get('POWERSHELL_VERSION', None)
|
||||||
if _powershell_version:
|
if _powershell_version:
|
||||||
_common_args = ['PowerShell', '-Version', _powershell_version] + _common_args[1:]
|
_common_args = ['PowerShell', '-Version', _powershell_version] + _common_args[1:]
|
||||||
|
|
||||||
def _escape(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 _encode_script(script, as_list=False):
|
|
||||||
'''Convert a PowerShell script to a single base64-encoded command.'''
|
|
||||||
script = '\n'.join([x.strip() for x in script.splitlines() if x.strip()])
|
|
||||||
encoded_script = base64.b64encode(script.encode('utf-16-le'))
|
|
||||||
cmd_parts = _common_args + ['-EncodedCommand', encoded_script]
|
|
||||||
if as_list:
|
|
||||||
return cmd_parts
|
|
||||||
return ' '.join(cmd_parts)
|
|
||||||
|
|
||||||
def _build_file_cmd(cmd_parts):
|
|
||||||
'''Build command line to run a file, given list of file name plus args.'''
|
|
||||||
return ' '.join(_common_args + ['-ExecutionPolicy', 'Unrestricted', '-File'] + ['"%s"' % x for x in cmd_parts])
|
|
||||||
|
|
||||||
class ShellModule(object):
|
class ShellModule(object):
|
||||||
|
|
||||||
def env_prefix(self, **kwargs):
|
def env_prefix(self, **kwargs):
|
||||||
|
@ -75,19 +48,19 @@ class ShellModule(object):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def remove(self, path, recurse=False):
|
def remove(self, path, recurse=False):
|
||||||
path = _escape(path)
|
path = self._escape(path)
|
||||||
if recurse:
|
if recurse:
|
||||||
return _encode_script('''Remove-Item "%s" -Force -Recurse;''' % path)
|
return self._encode_script('''Remove-Item "%s" -Force -Recurse;''' % path)
|
||||||
else:
|
else:
|
||||||
return _encode_script('''Remove-Item "%s" -Force;''' % path)
|
return self._encode_script('''Remove-Item "%s" -Force;''' % path)
|
||||||
|
|
||||||
def mkdtemp(self, basefile, system=False, mode=None):
|
def mkdtemp(self, basefile, system=False, mode=None):
|
||||||
basefile = _escape(basefile)
|
basefile = self._escape(basefile)
|
||||||
# FIXME: Support system temp path!
|
# FIXME: Support system temp path!
|
||||||
return _encode_script('''(New-Item -Type Directory -Path $env:temp -Name "%s").FullName | Write-Host -Separator '';''' % basefile)
|
return self._encode_script('''(New-Item -Type Directory -Path $env:temp -Name "%s").FullName | Write-Host -Separator '';''' % basefile)
|
||||||
|
|
||||||
def md5(self, path):
|
def md5(self, path):
|
||||||
path = _escape(path)
|
path = self._escape(path)
|
||||||
script = '''
|
script = '''
|
||||||
If (Test-Path -PathType Leaf "%(path)s")
|
If (Test-Path -PathType Leaf "%(path)s")
|
||||||
{
|
{
|
||||||
|
@ -105,15 +78,43 @@ class ShellModule(object):
|
||||||
Write-Host "1";
|
Write-Host "1";
|
||||||
}
|
}
|
||||||
''' % dict(path=path)
|
''' % dict(path=path)
|
||||||
return _encode_script(script)
|
return self._encode_script(script)
|
||||||
|
|
||||||
def build_module_command(self, env_string, shebang, cmd, rm_tmp=None):
|
def build_module_command(self, env_string, shebang, cmd, rm_tmp=None):
|
||||||
cmd = cmd.encode('utf-8')
|
cmd = cmd.encode('utf-8')
|
||||||
cmd_parts = shlex.split(cmd, posix=False)
|
cmd_parts = shlex.split(cmd, posix=False)
|
||||||
if not cmd_parts[0].lower().endswith('.ps1'):
|
if not cmd_parts[0].lower().endswith('.ps1'):
|
||||||
cmd_parts[0] = '%s.ps1' % cmd_parts[0]
|
cmd_parts[0] = '%s.ps1' % cmd_parts[0]
|
||||||
script = _build_file_cmd(cmd_parts)
|
script = self._build_file_cmd(cmd_parts)
|
||||||
if rm_tmp:
|
if rm_tmp:
|
||||||
rm_tmp = _escape(rm_tmp)
|
rm_tmp = self._escape(rm_tmp)
|
||||||
script = '%s; Remove-Item "%s" -Force -Recurse;' % (script, rm_tmp)
|
script = '%s; Remove-Item "%s" -Force -Recurse;' % (script, rm_tmp)
|
||||||
return _encode_script(script)
|
return self._encode_script(script)
|
||||||
|
|
||||||
|
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 _encode_script(self, script, as_list=False):
|
||||||
|
'''Convert a PowerShell script to a single base64-encoded command.'''
|
||||||
|
script = '\n'.join([x.strip() for x in script.splitlines() if x.strip()])
|
||||||
|
encoded_script = base64.b64encode(script.encode('utf-16-le'))
|
||||||
|
cmd_parts = _common_args + ['-EncodedCommand', encoded_script]
|
||||||
|
if as_list:
|
||||||
|
return cmd_parts
|
||||||
|
return ' '.join(cmd_parts)
|
||||||
|
|
||||||
|
def _build_file_cmd(self, cmd_parts):
|
||||||
|
'''Build command line to run a file, given list of file name plus args.'''
|
||||||
|
return ' '.join(_common_args + ['-ExecutionPolicy', 'Unrestricted', '-File'] + ['"%s"' % x for x in cmd_parts])
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,9 @@ class Display:
|
||||||
def vvvvv(self, msg, host=None):
|
def vvvvv(self, msg, host=None):
|
||||||
return self.verbose(msg, host=host, caplevel=4)
|
return self.verbose(msg, host=host, caplevel=4)
|
||||||
|
|
||||||
|
def vvvvvv(self, msg, host=None):
|
||||||
|
return self.verbose(msg, host=host, caplevel=5)
|
||||||
|
|
||||||
def verbose(self, msg, host=None, caplevel=2):
|
def verbose(self, msg, host=None, caplevel=2):
|
||||||
# FIXME: this needs to be implemented
|
# FIXME: this needs to be implemented
|
||||||
#msg = utils.sanitize_output(msg)
|
#msg = utils.sanitize_output(msg)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue