pywinrm-0.0.3/ 0000775 0001750 0001750 00000000000 12405714354 013460 5 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/ 0000775 0001750 0001750 00000000000 12405714354 014614 5 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/protocol.py 0000664 0001750 0001750 00000036157 12405700664 017042 0 ustar diyan diyan 0000000 0000000 import base64
from datetime import timedelta
import uuid
import xml.etree.ElementTree as ET
from isodate.isoduration import duration_isoformat
import xmltodict
from winrm.transport import HttpPlaintext, HttpKerberos, HttpSSL
class Protocol(object):
"""
This is the main class that does the SOAP request/response logic. There are a few helper classes, but pretty
much everything comes through here first.
"""
DEFAULT_TIMEOUT = 'PT60S'
DEFAULT_MAX_ENV_SIZE = 153600
DEFAULT_LOCALE = 'en-US'
def __init__(self, endpoint, transport='plaintext', username=None, password=None, realm=None, service=None, keytab=None, ca_trust_path=None, cert_pem=None, cert_key_pem=None):
"""
@param string endpoint: the WinRM webservice endpoint
@param string transport: transport type, one of 'kerberos' (default), 'ssl', 'plaintext'
@param string username: username
@param string password: password
@param string realm: the Kerberos realm we are authenticating to
@param string service: the service name, default is HTTP
@param string keytab: the path to a keytab file if you are using one
@param string ca_trust_path: Certification Authority trust path
@param string cert_pem: client authentication certificate file path in PEM format
@param string cert_key_pem: client authentication certificate key file path in PEM format
"""
self.endpoint = endpoint
self.timeout = Protocol.DEFAULT_TIMEOUT
self.max_env_sz = Protocol.DEFAULT_MAX_ENV_SIZE
self.locale = Protocol.DEFAULT_LOCALE
if transport == 'plaintext':
self.transport = HttpPlaintext(endpoint, username, password)
elif transport == 'kerberos':
self.transport = HttpKerberos(endpoint)
elif transport == 'ssl':
self.transport = HttpSSL(endpoint, username, password, cert_pem=cert_pem, cert_key_pem=cert_key_pem)
else:
raise NotImplementedError()
self.username = username
self.password = password
self.service = service
self.keytab = keytab
self.ca_trust_path = ca_trust_path
def set_timeout(self, seconds):
"""
Operation timeout, see http://msdn.microsoft.com/en-us/library/ee916629(v=PROT.13).aspx
@param int seconds: the number of seconds to set the timeout to. It will be converted to an ISO8601 format.
"""
# in original library there is an alias - op_timeout method
return duration_isoformat(timedelta(seconds))
def open_shell(self, i_stream='stdin', o_stream='stdout stderr', working_directory=None, env_vars=None, noprofile=False, codepage=437, lifetime=None, idle_timeout=None):
"""
Create a Shell on the destination host
@param string i_stream: Which input stream to open. Leave this alone unless you know what you're doing (default: stdin)
@param string o_stream: Which output stream to open. Leave this alone unless you know what you're doing (default: stdout stderr)
@param string working_directory: the directory to create the shell in
@param dict env_vars: environment variables to set for the shell. Fir instance: {'PATH': '%PATH%;c:/Program Files (x86)/Git/bin/', 'CYGWIN': 'nontsec codepage:utf8'}
@returns The ShellId from the SOAP response. This is our open shell instance on the remote machine.
@rtype string
"""
rq = {'env:Envelope': self._get_soap_header(
resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
action='http://schemas.xmlsoap.org/ws/2004/09/transfer/Create')}
header = rq['env:Envelope']['env:Header']
header['w:OptionSet'] = {
'w:Option': [
{
'@Name': 'WINRS_NOPROFILE',
'#text': str(noprofile).upper() #TODO remove str call
},
{
'@Name': 'WINRS_CODEPAGE',
'#text': str(codepage) #TODO remove str call
}
]
}
shell = rq['env:Envelope'].setdefault('env:Body', {}).setdefault('rsp:Shell', {})
shell['rsp:InputStreams'] = i_stream
shell['rsp:OutputStreams'] = o_stream
if working_directory:
#TODO ensure that rsp:WorkingDirectory should be nested within rsp:Shell
shell['rsp:WorkingDirectory'] = working_directory
# TODO: research Lifetime a bit more: http://msdn.microsoft.com/en-us/library/cc251546(v=PROT.13).aspx
#if lifetime:
# shell['rsp:Lifetime'] = iso8601_duration.sec_to_dur(lifetime)
# TODO: make it so the input is given in milliseconds and converted to xs:duration
if idle_timeout:
shell['rsp:IdleTimeOut'] = idle_timeout
if env_vars:
env = shell.setdefault('rsp:Environment', {})
for key, value in env_vars.items():
env['rsp:Variable'] = {'@Name': key, '#text': value}
rs = self.send_message(xmltodict.unparse(rq))
#rs = xmltodict.parse(rs)
#return rs['s:Envelope']['s:Body']['x:ResourceCreated']['a:ReferenceParameters']['w:SelectorSet']['w:Selector']['#text']
root = ET.fromstring(rs)
return next(node for node in root.findall('.//*') if node.get('Name') == 'ShellId').text
# Helper method for building SOAP Header
def _get_soap_header(self, action=None, resource_uri=None, shell_id=None, message_id=None):
if not message_id:
message_id = uuid.uuid4()
header = {
'@xmlns:xsd': 'http://www.w3.org/2001/XMLSchema',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'@xmlns:env': 'http://www.w3.org/2003/05/soap-envelope',
'@xmlns:a': 'http://schemas.xmlsoap.org/ws/2004/08/addressing',
'@xmlns:b': 'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd',
'@xmlns:n': 'http://schemas.xmlsoap.org/ws/2004/09/enumeration',
'@xmlns:x': 'http://schemas.xmlsoap.org/ws/2004/09/transfer',
'@xmlns:w': 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd',
'@xmlns:p': 'http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd',
'@xmlns:rsp': 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell',
'@xmlns:cfg': 'http://schemas.microsoft.com/wbem/wsman/1/config',
'env:Header': {
'a:To': 'http://windows-host:5985/wsman',
'a:ReplyTo': {
'a:Address': {
'@mustUnderstand': 'true',
'#text': 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous'
}
},
'w:MaxEnvelopeSize': {
'@mustUnderstand': 'true',
'#text': '153600'
},
'a:MessageID': 'uuid:{0}'.format(message_id),
'w:Locale': {
'@mustUnderstand': 'false',
'@xml:lang': 'en-US'
},
'p:DataLocale': {
'@mustUnderstand': 'false',
'@xml:lang': 'en-US'
},
# TODO: research this a bit http://msdn.microsoft.com/en-us/library/cc251561(v=PROT.13).aspx
#'cfg:MaxTimeoutms': 600
'w:OperationTimeout': 'PT60S',
'w:ResourceURI': {
'@mustUnderstand': 'true',
'#text': resource_uri
},
'a:Action': {
'@mustUnderstand': 'true',
'#text': action
}
}
}
if shell_id:
header['env:Header']['w:SelectorSet'] = {
'w:Selector': {
'@Name': 'ShellId',
'#text': shell_id
}
}
return header
def send_message(self, message):
# TODO add message_id vs relates_to checking
# TODO port error handling code
return self.transport.send_message(message)
def close_shell(self, shell_id):
"""
Close the shell
@param string shell_id: The shell id on the remote machine. See #open_shell
@returns This should have more error checking but it just returns true for now.
@rtype bool
"""
message_id = uuid.uuid4()
rq = {'env:Envelope': self._get_soap_header(
resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
action='http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete',
shell_id=shell_id,
message_id=message_id)}
# SOAP message requires empty env:Body
rq['env:Envelope'].setdefault('env:Body', {})
rs = self.send_message(xmltodict.unparse(rq))
root = ET.fromstring(rs)
relates_to = next(node for node in root.findall('.//*') if node.tag.endswith('RelatesTo')).text
# TODO change assert into user-friendly exception
assert uuid.UUID(relates_to.replace('uuid:', '')) == message_id
def run_command(self, shell_id, command, arguments=(), console_mode_stdin=True, skip_cmd_shell=False):
"""
Run a command on a machine with an open shell
@param string shell_id: The shell id on the remote machine. See #open_shell
@param string command: The command to run on the remote machine
@param iterable of string arguments: An array of arguments for this command
@param bool console_mode_stdin: (default: True)
@param bool skip_cmd_shell: (default: False)
@return: The CommandId from the SOAP response. This is the ID we need to query in order to get output.
@rtype string
"""
rq = {'env:Envelope': self._get_soap_header(
resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command',
shell_id=shell_id)}
header = rq['env:Envelope']['env:Header']
header['w:OptionSet'] = {
'w:Option': [
{
'@Name': 'WINRS_CONSOLEMODE_STDIN',
'#text': str(console_mode_stdin).upper()
},
{
'@Name': 'WINRS_SKIP_CMD_SHELL',
'#text': str(skip_cmd_shell).upper()
}
]
}
cmd_line = rq['env:Envelope'].setdefault('env:Body', {})\
.setdefault('rsp:CommandLine', {})
cmd_line['rsp:Command'] = {'#text': command}
if arguments:
cmd_line['rsp:Arguments'] = ' '.join(arguments)
rs = self.send_message(xmltodict.unparse(rq))
root = ET.fromstring(rs)
command_id = next(node for node in root.findall('.//*') if node.tag.endswith('CommandId')).text
return command_id
def cleanup_command(self, shell_id, command_id):
"""
Clean-up after a command. @see #run_command
@param string shell_id: The shell id on the remote machine. See #open_shell
@param string command_id: The command id on the remote machine. See #run_command
@returns: This should have more error checking but it just returns true for now.
@rtype bool
"""
message_id = uuid.uuid4()
rq = {'env:Envelope': self._get_soap_header(
resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal',
shell_id=shell_id,
message_id=message_id)}
# Signal the Command references to terminate (close stdout/stderr)
signal = rq['env:Envelope'].setdefault('env:Body', {}).setdefault('rsp:Signal', {})
signal['@CommandId'] = command_id
signal['rsp:Code'] = \
'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate'
rs = self.send_message(xmltodict.unparse(rq))
root = ET.fromstring(rs)
relates_to = next(node for node in root.findall('.//*') if node.tag.endswith('RelatesTo')).text
# TODO change assert into user-friendly exception
assert uuid.UUID(relates_to.replace('uuid:', '')) == message_id
def get_command_output(self, shell_id, command_id):
"""
Get the Output of the given shell and command
@param string shell_id: The shell id on the remote machine. See #open_shell
@param string command_id: The command id on the remote machine. See #run_command
#@return [Hash] Returns a Hash with a key :exitcode and :data. Data is an Array of Hashes where the cooresponding key
# is either :stdout or :stderr. The reason it is in an Array so so we can get the output in the order it ocurrs on
# the console.
"""
stdout_buffer, stderr_buffer = [], []
command_done = False
while not command_done:
stdout, stderr, return_code, command_done = \
self._raw_get_command_output(shell_id, command_id)
stdout_buffer.append(stdout)
stderr_buffer.append(stderr)
return ''.join(stdout_buffer), ''.join(stderr_buffer), return_code
def _raw_get_command_output(self, shell_id, command_id):
rq = {'env:Envelope': self._get_soap_header(
resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd',
action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive',
shell_id=shell_id)}
stream = rq['env:Envelope'].setdefault('env:Body', {}).setdefault('rsp:Receive', {})\
.setdefault('rsp:DesiredStream', {})
stream['@CommandId'] = command_id
stream['#text'] = 'stdout stderr'
rs = self.send_message(xmltodict.unparse(rq))
root = ET.fromstring(rs)
stream_nodes = [node for node in root.findall('.//*') if node.tag.endswith('Stream')]
stdout = stderr = ''
return_code = -1
for stream_node in stream_nodes:
if stream_node.text:
if stream_node.attrib['Name'] == 'stdout':
stdout += str(base64.b64decode(stream_node.text.encode('ascii')))
elif stream_node.attrib['Name'] == 'stderr':
stderr += str(base64.b64decode(stream_node.text.encode('ascii')))
# We may need to get additional output if the stream has not finished.
# The CommandState will change from Running to Done like so:
# @example
# from...
#
# to...
#
# 0
#
command_done = len([node for node in root.findall('.//*') if node.get('State', '').endswith('CommandState/Done')]) == 1
if command_done:
return_code = int(next(node for node in root.findall('.//*') if node.tag.endswith('ExitCode')).text)
return stdout, stderr, return_code, command_done
pywinrm-0.0.3/winrm/tests/ 0000775 0001750 0001750 00000000000 12405714354 015756 5 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/conftest.py 0000664 0001750 0001750 00000056156 12405704532 020167 0 ustar diyan diyan 0000000 0000000 import os
import json
import uuid
import xmltodict
from pytest import skip, fixture
from mock import patch
open_shell_request = """\
http://windows-host:5985/wsman
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
153600
uuid:11111111-1111-1111-1111-111111111111
PT60S
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
http://schemas.xmlsoap.org/ws/2004/09/transfer/Create
FALSE
437
stdin
stdout stderr
"""
open_shell_response = """\
http://schemas.xmlsoap.org/ws/2004/09/transfer/CreateResponse
uuid:11111111-1111-1111-1111-111111111112
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
uuid:11111111-1111-1111-1111-111111111111
http://windows-host:5985/wsman
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
11111111-1111-1111-1111-111111111113
"""
close_shell_request = """\
http://windows-host:5985/wsman
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
153600
uuid:11111111-1111-1111-1111-111111111111
PT60S
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete
11111111-1111-1111-1111-111111111113
"""
close_shell_response = """\
http://schemas.xmlsoap.org/ws/2004/09/transfer/DeleteResponse
uuid:11111111-1111-1111-1111-111111111112
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
uuid:11111111-1111-1111-1111-111111111111
"""
run_cmd_with_args_request = """\
http://windows-host:5985/wsman
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
153600
uuid:11111111-1111-1111-1111-111111111111
PT60S
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command
11111111-1111-1111-1111-111111111113
TRUE
FALSE
ipconfig
/all
"""
run_cmd_wo_args_request = """\
http://windows-host:5985/wsman
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
153600
uuid:11111111-1111-1111-1111-111111111111
PT60S
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command
11111111-1111-1111-1111-111111111113
TRUE
FALSE
hostname
"""
run_cmd_response = """\
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponse
uuid:11111111-1111-1111-1111-111111111112
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
uuid:11111111-1111-1111-1111-111111111111
11111111-1111-1111-1111-111111111114
"""
cleanup_cmd_request = """\
http://windows-host:5985/wsman
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
153600
uuid:11111111-1111-1111-1111-111111111111
PT60S
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal
11111111-1111-1111-1111-111111111113
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate
"""
cleanup_cmd_response = """\
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/SignalResponse
uuid:11111111-1111-1111-1111-111111111112
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
uuid:11111111-1111-1111-1111-111111111111
"""
get_cmd_output_request = """\
http://windows-host:5985/wsman
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
153600
uuid:11111111-1111-1111-1111-111111111111
PT60S
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive
11111111-1111-1111-1111-111111111113
stdout stderr
"""
get_cmd_output_response = """\
http://schemas.microsoft.com/wbem/wsman/1/windows/shell/ReceiveResponse
uuid:11111111-1111-1111-1111-111111111112
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
uuid:11111111-1111-1111-1111-111111111111
DQpXaW5kb3dzIElQIENvbmZpZ3VyYXRpb24NCg0K
ICAgSG9zdCBOYW1lIC4gLiAuIC4gLiAuIC4gLiAuIC4gLiAuIDogV0lORE9XUy1IT1NUCiAgIFByaW1hcnkgRG5zIFN1ZmZpeCAgLiAuIC4gLiAuIC4gLiA6IAogICBOb2RlIFR5cGUgLiAuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBIeWJyaWQKICAgSVAgUm91dGluZyBFbmFibGVkLiAuIC4gLiAuIC4gLiAuIDogTm8KICAgV0lOUyBQcm94eSBFbmFibGVkLiAuIC4gLiAuIC4gLiAuIDogTm8KCkV0aGVybmV0IGFkYXB0ZXIgTG9jYWwgQXJlYSBDb25uZWN0aW9uOgoKICAgQ29ubmVjdGlvbi1zcGVjaWZpYyBETlMgU3VmZml4ICAuIDogCiAgIERlc2NyaXB0aW9uIC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IEludGVsKFIpIDgyNTY3Vi0yIEdpZ2FiaXQgTmV0d29yayBDb25uZWN0aW9uCiAgIFBoeXNpY2FsIEFkZHJlc3MuIC4gLiAuIC4gLiAuIC4gLiA6IEY4LTBGLTQxLTE2LTg4LUU4CiAgIERIQ1AgRW5hYmxlZC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IE5vCiAgIEF1dG9jb25maWd1cmF0aW9uIEVuYWJsZWQgLiAuIC4gLiA6IFllcwogICBMaW5rLWxvY2FsIElQdjYgQWRkcmVzcyAuIC4gLiAuIC4gOiBmZTgwOjphOTkwOjM1ZTM6YTZhYjpmYzE1JTEwKFByZWZlcnJlZCkgCiAgIElQdjQgQWRkcmVzcy4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IDE3My4xODUuMTUzLjkzKFByZWZlcnJlZCkgCiAgIFN1Ym5ldCBNYXNrIC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IDI1NS4yNTUuMjU1LjI0OAogICBEZWZhdWx0IEdhdGV3YXkgLiAuIC4gLiAuIC4gLiAuIC4gOiAxNzMuMTg1LjE1My44OQogICBESENQdjYgSUFJRCAuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiAyNTExMzc4NTcKICAgREhDUHY2IENsaWVudCBEVUlELiAuIC4gLiAuIC4gLiAuIDogMDAtMDEtMDAtMDEtMTYtM0ItM0YtQzItRjgtMEYtNDEtMTYtODgtRTgKICAgRE5TIFNlcnZlcnMgLiAuIC4gLiAuIC4gLiAuIC4gLiAuIDogMjA3LjkxLjUuMzIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjA4LjY3LjIyMi4yMjIKICAgTmV0QklPUyBvdmVyIFRjcGlwLiAuIC4gLiAuIC4gLiAuIDogRW5hYmxlZAoKRXRoZXJuZXQgYWRhcHRlciBMb2NhbCBBcmVhIENvbm5lY3Rpb24qIDk6CgogICBNZWRpYSBTdGF0ZSAuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBNZWRpYSBkaXNjb25uZWN0ZWQKICAgQ29ubmVjdGlvbi1zcGVjaWZpYyBETlMgU3VmZml4ICAuIDogCiAgIERlc2NyaXB0aW9uIC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IEp1bmlwZXIgTmV0d29yayBDb25uZWN0IFZpcnR1YWwgQWRhcHRlcgogICBQaHlzaWNhbCBBZGRyZXNzLiAuIC4gLiAuIC4gLiAuIC4gOiAwMC1GRi1BMC04My00OC0wNAogICBESENQIEVuYWJsZWQuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBZZXMKICAgQXV0b2NvbmZpZ3VyYXRpb24gRW5hYmxlZCAuIC4gLiAuIDogWWVzCgpUdW5uZWwgYWRhcHRlciBpc2F0YXAue0FBNDI2QjM3LTM2OTUtNEVCOC05OTBGLTRDRkFDODQ1RkQxN306CgogICBNZWRpYSBTdGF0ZSAuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBNZWRpYSBkaXNjb25uZWN0ZWQKICAgQ29ubmVjdGlvbi1zcGVjaWZpYyBETlMgU3VmZml4ICAuIDogCiAgIERlc2NyaXB0aW9uIC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IE1pY3Jvc29mdCBJU0FUQVAgQWRhcHRlcgogICBQaHlzaWNhbCBBZGRyZXNzLiAuIC4gLiAuIC4gLiAuIC4gOiAwMC0wMC0wMC0wMC0wMC0wMC0wMC1FMAogICBESENQIEVuYWJsZWQuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBObwogICBBdXRvY29uZmlndXJhdGlvbiBFbmFibGVkIC4gLiAuIC4gOiBZZXMKClR1bm5lbCBhZGFwdGVyIFRlcmVkbyBUdW5uZWxpbmcgUHNldWRvLUludGVyZmFjZToKCiAgIENvbm5lY3Rpb24tc3BlY2lmaWMgRE5TIFN1ZmZpeCAgLiA6IAogICBEZXNjcmlwdGlvbiAuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBUZXJlZG8gVHVubmVsaW5nIFBzZXVkby1JbnRlcmZhY2UKICAgUGh5c2ljYWwgQWRkcmVzcy4gLiAuIC4gLiAuIC4gLiAuIDogMDAtMDAtMDAtMDAtMDAtMDAtMDAtRTAKICAgREhDUCBFbmFibGVkLiAuIC4gLiAuIC4gLiAuIC4gLiAuIDogTm8KICAgQXV0b2NvbmZpZ3VyYXRpb24gRW5hYmxlZCAuIC4gLiAuIDogWWVzCiAgIElQdjYgQWRkcmVzcy4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IDIwMDE6MDo5ZDM4Ojk1M2M6MmNlZjo3ZmM6NTI0Njo2NmEyKFByZWZlcnJlZCkgCiAgIExpbmstbG9jYWwgSVB2NiBBZGRyZXNzIC4gLiAuIC4gLiA6IGZlODA6OjJjZWY6N2ZjOjUyNDY6NjZhMiUxMyhQcmVmZXJyZWQpIAogICBEZWZhdWx0IEdhdGV3YXkgLiAuIC4gLiAuIC4gLiAuIC4gOiAKICAgTmV0QklPUyBvdmVyIFRjcGlwLiAuIC4gLiAuIC4gLiAuIDogRGlzYWJsZWQKClR1bm5lbCBhZGFwdGVyIDZUTzQgQWRhcHRlcjoKCiAgIENvbm5lY3Rpb24tc3BlY2lmaWMgRE5TIFN1ZmZpeCAgLiA6IAogICBEZXNjcmlwdGlvbiAuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiBNaWNyb3NvZnQgNnRvNCBBZGFwdGVyICMyCiAgIFBoeXNpY2FsIEFkZHJlc3MuIC4gLiAuIC4gLiAuIC4gLiA6IDAwLTAwLTAwLTAwLTAwLTAwLTAwLUUwCiAgIERIQ1AgRW5hYmxlZC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IE5vCiAgIEF1dG9jb25maWd1cmF0aW9uIEVuYWJsZWQgLiAuIC4gLiA6IFllcwogICBJUHY2IEFkZHJlc3MuIC4gLiAuIC4gLiAuIC4gLiAuIC4gOiAyMDAyOmFkYjk6OTk1ZDo6YWRiOTo5OTVkKFByZWZlcnJlZCkgCiAgIERlZmF1bHQgR2F0ZXdheSAuIC4gLiAuIC4gLiAuIC4gLiA6IDIwMDI6YzA1ODo2MzAxOjpjMDU4OjYzMDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjAwMjpjMDU4OjYzMDE6OjEKICAgRE5TIFNlcnZlcnMgLiAuIC4gLiAuIC4gLiAuIC4gLiAuIDogMjA3LjkxLjUuMzIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjA4LjY3LjIyMi4yMjIKICAgTmV0QklPUyBvdmVyIFRjcGlwLiAuIC4gLiAuIC4gLiAuIDogRGlzYWJsZWQKClR1bm5lbCBhZGFwdGVyIGlzYXRhcC57QkExNjBGQzUtNzAyOC00QjFGLUEwNEItMUFDODAyQjBGRjVBfToKCiAgIE1lZGlhIFN0YXRlIC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IE1lZGlhIGRpc2Nvbm5lY3RlZAogICBDb25uZWN0aW9uLXNwZWNpZmljIEROUyBTdWZmaXggIC4gOiAKICAgRGVzY3JpcHRpb24gLiAuIC4gLiAuIC4gLiAuIC4gLiAuIDogTWljcm9zb2Z0IElTQVRBUCBBZGFwdGVyICMyCiAgIFBoeXNpY2FsIEFkZHJlc3MuIC4gLiAuIC4gLiAuIC4gLiA6IDAwLTAwLTAwLTAwLTAwLTAwLTAwLUUwCiAgIERIQ1AgRW5hYmxlZC4gLiAuIC4gLiAuIC4gLiAuIC4gLiA6IE5vCiAgIEF1dG9jb25maWd1cmF0aW9uIEVuYWJsZWQgLiAuIC4gLiA6IFllcwo=
0
"""
def sort_dict(ordered_dict):
items = sorted(ordered_dict.items(), key=lambda x: x[0])
ordered_dict.clear()
for key, value in items:
if isinstance(value, dict):
sort_dict(value)
ordered_dict[key] = value
def xml_str_compare(first, second):
first_dict = xmltodict.parse(first)
second_dict = xmltodict.parse(second)
sort_dict(first_dict)
sort_dict(second_dict)
return first_dict == second_dict
class TransportStub(object):
def send_message(self, message):
if xml_str_compare(message, open_shell_request):
return open_shell_response
elif xml_str_compare(message, close_shell_request):
return close_shell_response
elif xml_str_compare(
message, run_cmd_with_args_request) or xml_str_compare(
message, run_cmd_wo_args_request):
return run_cmd_response
elif xml_str_compare(message, cleanup_cmd_request):
return cleanup_cmd_response
elif xml_str_compare(message, get_cmd_output_request):
return get_cmd_output_response
else:
raise Exception('Message was not expected')
@fixture(scope='module')
def protocol_fake(request):
uuid4_patcher = patch('uuid.uuid4')
uuid4_mock = uuid4_patcher.start()
uuid4_mock.return_value = uuid.UUID(
'11111111-1111-1111-1111-111111111111')
from winrm.protocol import Protocol
protocol_fake = Protocol(
endpoint='http://windows-host:5985/wsman',
transport='plaintext',
username='john.smith',
password='secret')
protocol_fake.transport = TransportStub()
def uuid4_patch_stop():
uuid4_patcher.stop()
request.addfinalizer(uuid4_patch_stop)
return protocol_fake
@fixture(scope='module')
def protocol_real():
endpoint = os.environ.get('WINRM_ENDPOINT', None)
transport = os.environ.get('WINRM_TRANSPORT', None)
username = os.environ.get('WINRM_USERNAME', None)
password = os.environ.get('WINRM_PASSWORD', None)
if endpoint:
# TODO consider replace json with yaml for integration test settings
# TODO json does not support comments
settings = {'endpoint': endpoint}
if transport:
settings['transport'] = transport
if username:
settings['username'] = username
if password:
settings['password'] = password
from winrm.protocol import Protocol
protocol = Protocol(**settings)
return protocol
else:
skip('WINRM_ENDPOINT environment variable was not set. Integration tests will be skipped') pywinrm-0.0.3/winrm/tests/test_session.py 0000664 0001750 0001750 00000002520 12405700664 021050 0 ustar diyan diyan 0000000 0000000 from winrm import Session
def test_run_cmd(protocol_fake):
#TODO this test should cover __init__ method
s = Session('windows-host', auth=('john.smith', 'secret'))
s.protocol = protocol_fake
r = s.run_cmd('ipconfig', ['/all'])
assert r.status_code == 0
assert 'Windows IP Configuration' in r.std_out
assert len(r.std_err) == 0
def test_target_as_hostname():
s = Session('windows-host', auth=('john.smith', 'secret'))
assert s.url == 'http://windows-host:5985/wsman'
def test_target_as_hostname_then_port():
s = Session('windows-host:1111', auth=('john.smith', 'secret'))
assert s.url == 'http://windows-host:1111/wsman'
def test_target_as_schema_then_hostname():
s = Session('http://windows-host', auth=('john.smith', 'secret'))
assert s.url == 'http://windows-host:5985/wsman'
def test_target_as_schema_then_hostname_then_port():
s = Session('http://windows-host:1111', auth=('john.smith', 'secret'))
assert s.url == 'http://windows-host:1111/wsman'
def test_target_as_full_url():
s = Session('http://windows-host:1111/wsman', auth=('john.smith', 'secret'))
assert s.url == 'http://windows-host:1111/wsman'
def test_target_with_dots():
s = Session('windows-host.example.com', auth=('john.smith', 'secret'))
assert s.url == 'http://windows-host.example.com:5985/wsman'
pywinrm-0.0.3/winrm/tests/test_powershell.py 0000664 0001750 0001750 00000000000 12405700664 021540 0 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/test_cmd.py 0000664 0001750 0001750 00000000000 12405700664 020117 0 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/test_integration_protocol.py 0000664 0001750 0001750 00000004222 12405700664 023632 0 ustar diyan diyan 0000000 0000000 import re
import pytest
xfail = pytest.mark.xfail
def test_open_shell_and_close_shell(protocol_real):
shell_id = protocol_real.open_shell()
assert re.match('^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$', shell_id)
protocol_real.close_shell(shell_id)
def test_run_command_with_arguments_and_cleanup_command(protocol_real):
shell_id = protocol_real.open_shell()
command_id = protocol_real.run_command(shell_id, 'ipconfig', ['/all'])
assert re.match('^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$', command_id)
protocol_real.cleanup_command(shell_id, command_id)
protocol_real.close_shell(shell_id)
def test_run_command_without_arguments_and_cleanup_command(protocol_real):
shell_id = protocol_real.open_shell()
command_id = protocol_real.run_command(shell_id, 'hostname')
assert re.match('^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$', command_id)
protocol_real.cleanup_command(shell_id, command_id)
protocol_real.close_shell(shell_id)
def test_get_command_output(protocol_real):
shell_id = protocol_real.open_shell()
command_id = protocol_real.run_command(shell_id, 'ipconfig', ['/all'])
std_out, std_err, status_code = protocol_real.get_command_output(
shell_id, command_id)
assert status_code == 0
assert 'Windows IP Configuration' in std_out
assert len(std_err) == 0
protocol_real.cleanup_command(shell_id, command_id)
protocol_real.close_shell(shell_id)
def test_run_command_taking_more_than_60_seconds(protocol_real):
shell_id = protocol_real.open_shell()
command_id = protocol_real.run_command(shell_id, 'PowerShell -Command Start-Sleep -s 75')
assert re.match('^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$', command_id)
std_out, std_err, status_code = protocol_real.get_command_output(
shell_id, command_id)
assert status_code == 0
assert len(std_err) == 0
protocol_real.cleanup_command(shell_id, command_id)
protocol_real.close_shell(shell_id)
@xfail()
def test_set_timeout(protocol_real):
raise NotImplementedError()
@xfail()
def test_set_max_env_size(protocol_real):
raise NotImplementedError()
@xfail()
def test_set_locale(protocol_real):
raise NotImplementedError() pywinrm-0.0.3/winrm/tests/test_nori_type_casting.py 0000664 0001750 0001750 00000000000 12405700664 023074 0 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/__init__.py 0000664 0001750 0001750 00000000000 12405700664 020054 0 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/test_wql.py 0000664 0001750 0001750 00000000000 12405700664 020157 0 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/test_protocol.py 0000664 0001750 0001750 00000002506 12405700664 021232 0 ustar diyan diyan 0000000 0000000 def test_open_shell_and_close_shell(protocol_fake):
shell_id = protocol_fake.open_shell()
assert shell_id == '11111111-1111-1111-1111-111111111113'
protocol_fake.close_shell(shell_id)
def test_run_command_with_arguments_and_cleanup_command(protocol_fake):
shell_id = protocol_fake.open_shell()
command_id = protocol_fake.run_command(shell_id, 'ipconfig', ['/all'])
assert command_id == '11111111-1111-1111-1111-111111111114'
protocol_fake.cleanup_command(shell_id, command_id)
protocol_fake.close_shell(shell_id)
def test_run_command_without_arguments_and_cleanup_command(protocol_fake):
shell_id = protocol_fake.open_shell()
command_id = protocol_fake.run_command(shell_id, 'hostname')
assert command_id == '11111111-1111-1111-1111-111111111114'
protocol_fake.cleanup_command(shell_id, command_id)
protocol_fake.close_shell(shell_id)
def test_get_command_output(protocol_fake):
shell_id = protocol_fake.open_shell()
command_id = protocol_fake.run_command(shell_id, 'ipconfig', ['/all'])
std_out, std_err, status_code = protocol_fake.get_command_output(shell_id, command_id)
assert status_code == 0
assert 'Windows IP Configuration' in std_out
assert len(std_err) == 0
protocol_fake.cleanup_command(shell_id, command_id)
protocol_fake.close_shell(shell_id) pywinrm-0.0.3/winrm/tests/sample_script.ps1 0000664 0001750 0001750 00000000000 12405700664 021235 0 ustar diyan diyan 0000000 0000000 pywinrm-0.0.3/winrm/tests/test_integration_session.py 0000664 0001750 0001750 00000000200 12405700664 023444 0 ustar diyan diyan 0000000 0000000 import pytest
from winrm import Session
xfail = pytest.mark.xfail
@xfail()
def test_run_cmd():
raise NotImplementedError() pywinrm-0.0.3/winrm/transport.py 0000664 0001750 0001750 00000023505 12405704532 017224 0 ustar diyan diyan 0000000 0000000 import sys
import base64
from winrm.exceptions import WinRMTransportError, UnauthorizedError
HAVE_KERBEROS = False
try:
import kerberos
HAVE_KERBEROS = True
except ImportError:
pass
is_py2 = sys.version[0] == '2'
if is_py2:
from urllib2 import Request, URLError, HTTPError, HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, HTTPSHandler
from urllib2 import urlopen, build_opener, install_opener
from urlparse import urlparse
from httplib import HTTPSConnection
else:
from urllib.request import Request, URLError, HTTPError, HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, HTTPSHandler
from urllib.request import urlopen, build_opener, install_opener
from urllib.parse import urlparse
from http.client import HTTPSConnection
class HttpTransport(object):
def __init__(self, endpoint, username, password):
self.endpoint = endpoint
self.username = username
self.password = password
self.user_agent = 'Python WinRM client'
self.timeout = 3600 # Set this to an unreasonable amount for now because WinRM has timeouts
def basic_auth_only(self):
#here we should remove handler for any authentication handlers other than basic
# but maybe leave original credentials
# auths = @httpcli.www_auth.instance_variable_get('@authenticator')
# auths.delete_if {|i| i.scheme !~ /basic/i}
# drop all variables in auths if they not contains "basic" as insensitive.
pass
def no_sspi_auth(self):
# here we should remove handler for Negotiate/NTLM negotiation
# but maybe leave original credentials
pass
class HttpPlaintext(HttpTransport):
def __init__(self, endpoint, username='', password='', disable_sspi=True, basic_auth_only=True):
super(HttpPlaintext, self).__init__(endpoint, username, password)
if disable_sspi:
self.no_sspi_auth()
if basic_auth_only:
self.basic_auth_only()
self._headers = {'Content-Type': 'application/soap+xml;charset=UTF-8',
'User-Agent': 'Python WinRM client'}
def _setup_opener(self):
password_manager = HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, self.endpoint, self.username, self.password)
auth_manager = HTTPBasicAuthHandler(password_manager)
opener = build_opener(auth_manager)
install_opener(opener)
def send_message(self, message):
headers = self._headers.copy()
headers['Content-Length'] = len(message)
self._setup_opener()
request = Request(self.endpoint, data=message, headers=headers)
try:
response = urlopen(request, timeout=self.timeout)
# Version 1.1 of WinRM adds the namespaces in the document instead of the envelope so we have to
# add them ourselves here. This should have no affect version 2.
response_text = response.read()
return response_text
#doc = ElementTree.fromstring(response.read())
#Ruby
#doc = Nokogiri::XML(resp.http_body.content)
#doc.collect_namespaces.each_pair do |k,v|
# doc.root.add_namespace((k.split(/:/).last),v) unless doc.namespaces.has_key?(k)
#end
#return doc
#return doc
except HTTPError as ex:
if ex.code == 401:
raise UnauthorizedError(transport='plaintext', message=ex.msg)
response_text = ex.read()
# Per http://msdn.microsoft.com/en-us/library/cc251676.aspx rule 3,
# should handle this 500 error and retry receiving command output.
if 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and 'Code="2150858793"' in response_text:
# TODO raise TimeoutError here instead of just return text
return response_text
error_message = 'Bad HTTP response returned from server. Code {0}'.format(ex.code)
if ex.msg:
error_message += ', {0}'.format(ex.msg)
raise WinRMTransportError('http', error_message)
except URLError as ex:
raise WinRMTransportError('http', ex.reason)
class HTTPSClientAuthHandler(HTTPSHandler):
def __init__(self, cert, key):
HTTPSHandler.__init__(self)
self.cert = cert
self.key = key
def https_open(self, req):
return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout=300):
return HTTPSConnection(host, key_file=self.key, cert_file=self.cert)
class HttpSSL(HttpPlaintext):
"""Uses SSL to secure the transport"""
def __init__(self, endpoint, username, password, ca_trust_path=None, disable_sspi=True, basic_auth_only=True,
cert_pem=None, cert_key_pem=None):
super(HttpSSL, self).__init__(endpoint, username, password)
self._cert_pem = cert_pem
self._cert_key_pem = cert_key_pem
#Ruby
#@httpcli.set_auth(endpoint, user, pass)
#@httpcli.ssl_config.set_trust_ca(ca_trust_path) unless ca_trust_path.nil?
if disable_sspi:
self.no_sspi_auth()
if basic_auth_only:
self.basic_auth_only()
if self._cert_pem:
self._headers['Authorization'] = "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
def _setup_opener(self):
if not self._cert_pem:
super(HttpSSL, self)._setup_opener()
else:
opener = build_opener(HTTPSClientAuthHandler(self._cert_pem, self._cert_key_pem))
install_opener(opener)
class KerberosTicket:
"""
Implementation based on http://ncoghlan_devs-python-notes.readthedocs.org/en/latest/python_kerberos.html
"""
def __init__(self, service):
ignored_code, krb_context = kerberos.authGSSClientInit(service)
kerberos.authGSSClientStep(krb_context, '')
# TODO authGSSClientStep may raise following error:
#GSSError: (('Unspecified GSS failure. Minor code may provide more information', 851968),
# ("Credentials cache file '/tmp/krb5cc_1000' not found", -1765328189))
self._krb_context = krb_context
gss_response = kerberos.authGSSClientResponse(krb_context)
self.auth_header = 'Negotiate {0}'.format(gss_response)
def verify_response(self, auth_header):
# Handle comma-separated lists of authentication fields
for field in auth_header.split(','):
kind, ignored_space, details = field.strip().partition(' ')
if kind.lower() == 'negotiate':
auth_details = details.strip()
break
else:
raise ValueError('Negotiate not found in {0}'.format(auth_header))
# Finish the Kerberos handshake
krb_context = self._krb_context
if krb_context is None:
raise RuntimeError('Ticket already used for verification')
self._krb_context = None
kerberos.authGSSClientStep(krb_context, auth_details)
#print('User {0} authenticated successfully using Kerberos authentication'.format(kerberos.authGSSClientUserName(krb_context)))
kerberos.authGSSClientClean(krb_context)
class HttpKerberos(HttpTransport):
def __init__(self, endpoint, realm=None, service='HTTP', keytab=None):
"""
Uses Kerberos/GSS-API to authenticate and encrypt messages
@param string endpoint: the WinRM webservice endpoint
@param string realm: the Kerberos realm we are authenticating to
@param string service: the service name, default is HTTP
@param string keytab: the path to a keytab file if you are using one
"""
if not HAVE_KERBEROS:
raise WinRMTransportError('kerberos is not installed')
super(HttpKerberos, self).__init__(endpoint, None, None)
self.krb_service = '{0}@{1}'.format(service, urlparse(endpoint).hostname)
#self.krb_ticket = KerberosTicket(krb_service)
def set_auth(self, username, password):
raise NotImplementedError
def send_message(self, message):
# TODO current implementation does negotiation on each HTTP request which is not efficient
# TODO support kerberos session with message encryption
krb_ticket = KerberosTicket(self.krb_service)
headers = {'Authorization': krb_ticket.auth_header,
'Connection': 'Keep-Alive',
'Content-Type': 'application/soap+xml;charset=UTF-8',
'User-Agent': 'Python WinRM client'}
request = Request(self.endpoint, data=message, headers=headers)
try:
response = urlopen(request, timeout=self.timeout)
krb_ticket.verify_response(response.headers['WWW-Authenticate'])
response_text = response.read()
return response_text
except HTTPError as ex:
response_text = ex.read()
# Per http://msdn.microsoft.com/en-us/library/cc251676.aspx rule 3,
# should handle this 500 error and retry receiving command output.
if 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and 'Code="2150858793"' in response_text:
return response_text
#if ex.code == 401 and ex.headers['WWW-Authenticate'] == 'Negotiate, Basic realm="WSMAN"':
error_message = 'Kerberos-based authentication was failed. Code {0}'.format(ex.code)
if ex.msg:
error_message += ', {0}'.format(ex.msg)
raise WinRMTransportError('kerberos', error_message)
except URLError as ex:
raise WinRMTransportError('kerberos', ex.reason)
def _winrm_encrypt(self, string):
"""
@returns the encrypted request string
@rtype string
"""
raise NotImplementedError
def _winrm_decrypt(self, string):
raise NotImplementedError
pywinrm-0.0.3/winrm/__init__.py 0000664 0001750 0001750 00000010431 12405700664 016723 0 ustar diyan diyan 0000000 0000000 import re
import base64
import xml.etree.ElementTree as ET
from winrm.protocol import Protocol
class Response(object):
"""Response from a remote command execution"""
def __init__(self, args):
self.std_out, self.std_err, self.status_code = args
def __repr__(self):
#TODO put tree dots at the end if out/err was truncated
return ''.format(
self.status_code, self.std_out[:20], self.std_err[:20])
class Session(object):
#TODO implement context manager methods
def __init__(self, target, auth, transport='plaintext'):
username, password = auth
self.url = self._build_url(target, transport)
self.protocol = Protocol(self.url, transport=transport, username=username, password=password)
def run_cmd(self, command, args=()):
#TODO optimize perf. Do not call open/close shell every time
shell_id = self.protocol.open_shell()
command_id = self.protocol.run_command(shell_id, command, args)
rs = Response(self.protocol.get_command_output(shell_id, command_id))
self.protocol.cleanup_command(shell_id, command_id)
self.protocol.close_shell(shell_id)
return rs
def run_ps(self, script):
"""base64 encodes a Powershell script and executes the powershell encoded script command"""
# must use utf16 little endian on windows
base64_script = base64.b64encode(script.encode("utf_16_le"))
rs = self.run_cmd("powershell -encodedcommand %s" % (base64_script))
if len(rs.std_err):
# if there was an error message, clean it it up and make it human readable
rs.std_err = self.clean_error_msg(rs.std_err)
return rs
def clean_error_msg(self, msg):
"""converts a Powershell CLIXML message to a more human readable string"""
# if the msg does not start with this, return it as is
if msg.startswith("#< CLIXML\r\n"):
# for proper xml, we need to remove the CLIXML part (the first line)
msg_xml = msg[11:]
print(">%s<" % msg_xml)
try:
# remove the namespaces from the xml for easier processing
msg_xml = self.strip_namespace(msg_xml)
root = ET.fromstring(msg_xml)
# the S node is the error message, find all S nodes
nodes = root.findall("./S")
new_msg = ""
for s in nodes:
# append error msg string to result, also
# the hex chars represent CRLF so we replace with newline
new_msg += s.text.replace("_x000D__x000A_","\n")
except Exception as e:
# if any of the above fails, the msg was not true xml
# print a warning and return the orignal string
print("Warning: there was a problem converting the Powershell error message: %s" % (e))
else:
# if new_msg was populated, that's our error message
# otherwise the original error message will be used
if len(new_msg):
# remove leading and trailing whitespace while we are here
msg = new_msg.strip()
return msg
def strip_namespace(self, xml):
"""strips any namespaces from an xml string"""
try:
p = re.compile("xmlns=*[\"\"][^\"\"]*[\"\"]")
allmatches = p.finditer(xml)
for match in allmatches:
xml = xml.replace(match.group(), "")
return xml
except Exception as e:
raise Exception(e)
@staticmethod
def _build_url(target, transport):
match = re.match(
'(?i)^((?Phttp[s]?)://)?(?P[0-9a-z-_.]+)(:(?P\d+))?(?P(/)?(wsman)?)?', target)
scheme = match.group('scheme')
if not scheme:
scheme = 'https' if transport == 'ssl' else 'http' # TODO do we have anything other than HTTP/HTTPS
host = match.group('host')
port = match.group('port')
if not port:
port = 5986 if transport == 'ssl' else 5985
path = match.group('path')
if not path:
path = 'wsman'
return '{0}://{1}:{2}/{3}'.format(scheme, host, port, path.lstrip('/'))
pywinrm-0.0.3/winrm/exceptions.py 0000664 0001750 0001750 00000001707 12405700664 017353 0 ustar diyan diyan 0000000 0000000 import re
class WinRMWebServiceError(Exception):
"""Generic WinRM SOAP Error"""
pass
class WinRMAuthorizationError(Exception):
"""Authorization Error"""
pass
class WinRMWSManFault(Exception):
"""A Fault returned in the SOAP response. The XML node is a WSManFault"""
pass
class WinRMTransportError(Exception):
""""Transport-level error"""
code = 500
def __init__(self, transport, message):
self.transport = transport
self.message = message
def __str__(self):
return '{0} {1}. {2}'.format(self.code, re.sub('Error$', '', self.__class__.__name__), self.message)
def __repr__(self):
return "{0}(code={1}, transport='{2}', message='{3}')".format(
self.__class__.__name__, self.code, self.transport, self.message)
class UnauthorizedError(WinRMTransportError):
"""Raise if the user is not authorized"""
code = 401
class TimeoutError(WinRMTransportError):
pass pywinrm-0.0.3/setup.py 0000664 0001750 0001750 00000003254 12405713372 015175 0 ustar diyan diyan 0000000 0000000 from distutils.core import setup
__version__ = '0.0.3'
project_name = 'pywinrm'
# PyPi supports only reStructuredText, so pandoc should be installed
# before uploading package
try:
import pypandoc
long_description = pypandoc.convert('README.md', 'rst')
except ImportError:
long_description = ''
setup(
name=project_name,
version=__version__,
description='Python library for Windows Remote Management',
long_description=long_description,
keywords='winrm ws-man devops ws-management'.split(' '),
author='Alexey Diyan',
author_email='alexey.diyan@gmail.com',
url='http://github.com/diyan/pywinrm/',
license='MIT license',
packages=('winrm', 'winrm.tests'),
package_data={'winrm.tests': ['*.ps1']},
install_requires=['xmltodict', 'isodate'],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: Developers',
'Intended Audience :: System Administrators',
'Natural Language :: English',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: System :: Clustering',
'Topic :: System :: Distributed Computing',
'Topic :: System :: Systems Administration'
],
)
pywinrm-0.0.3/PKG-INFO 0000664 0001750 0001750 00000014727 12405714354 014570 0 ustar diyan diyan 0000000 0000000 Metadata-Version: 1.1
Name: pywinrm
Version: 0.0.3
Summary: Python library for Windows Remote Management
Home-page: http://github.com/diyan/pywinrm/
Author: Alexey Diyan
Author-email: alexey.diyan@gmail.com
License: MIT license
Description: pywinrm |Build Status| |Coverage Status|
========================================
pywinrm is a Python client for Windows Remote Management (WinRM). This
allows you to invoke commands on target Windows machines from any
machine that can run Python.
WinRM allows you to call native objects in Windows. This includes, but
is not limited to, running batch scripts, powershell scripts and
fetching WMI variables. For more information on WinRM, please visit
`Microsoft's WinRM
site `__.
Requirements
------------
- Linux, Mac OS X or Windows
- CPython 2.6, 2.7, 3.2, 3.3 or PyPy 1.9
- `python-kerberos `__ is
optional
Installation
------------
To install pywinrm, simply
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: bash
$ pip install http://github.com/diyan/pywinrm/archive/master.zip
To use Kerberos authentication you need optional dependency
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: bash
$ sudo apt-get install python-dev libkrb5-dev
$ pip install kerberos
Example Usage
-------------
Run process on remote host
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
import winrm
s = winrm.Session('windows-host.example.com', auth=('john.smith', 'secret'))
r = s.run_cmd('ipconfig', ['/all'])
>>> r.status_code
0
>>> r.std_out
Windows IP Configuration
Host Name . . . . . . . . . . . . : WINDOWS-HOST
Primary Dns Suffix . . . . . . . :
Node Type . . . . . . . . . . . . : Hybrid
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No
...
>>> r.std_err
NOTE pywirnm with try guess correct endpoint url from various formats:
- windows-host -> http://windows-host:5985/wsman
- windows-host:1111 -> http://windows-host:1111/wsman
- http://windows-host -> http://windows-host:5985/wsman
- http://windows-host:1111 -> http://windows-host:1111/wsman
- http://windows-host:1111/wsman -> http://windows-host:1111/wsman
Run Powershell on remote host
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
import winrm
ps_script = """$strComputer = $Host
Clear
$RAM = WmiObject Win32_ComputerSystem
$MB = 1048576
"Installed Memory: " + [int]($RAM.TotalPhysicalMemory /$MB) + " MB" """
s = winrm.Session('windows-host.example.com', auth=('john.smith', 'secret'))
r = s.run_ps(ps_script)
>>> r.status_code
0
>>> r.std_out
Installed Memory: 3840 MB
>>> r.std_err
Powershell script will be base64 UTF16 little endian encoded prior to
sending to Windows host. Error messages are converted from the
Powershell CLIXML format to a human readable format as a convenience.
Run process with low-level API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
from winrm.protocol import Protocol
p = Protocol(
endpoint='http://windows-host:5985/wsman',
transport='plaintext',
username='john.smith',
password='secret')
shell_id = p.open_shell()
command_id = p.run_command(shell_id, 'ipconfig', ['/all'])
std_out, std_err, status_code = p.get_command_output(shell_id, command_id)
p.cleanup_command(shell_id, command_id)
p.close_shell(shell_id)
Enable WinRM on remote host
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Enable basic WinRM authentication (good only for troubleshooting, for
hosts in domain better to use Kerberos authentication)
- Allow unencrypted message passing over WinRM (not secure for hosts in
domain but this feature is not implemented so far)
::
winrm set winrm/config/client/auth @{Basic="true"}
winrm set winrm/config/service/auth @{Basic="true"}
winrm set winrm/config/service @{AllowUnencrypted="true"}
Contributors (alphabetically)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Alessandro Pilotti
- David Cournapeau
- Gema Gomez
- Matt Clark
- Patrick Dunnigan
Want to help - send a pull request. I will accept good pull requests for
sure.
.. |Build Status| image:: https://travis-ci.org/diyan/pywinrm.png
:target: https://travis-ci.org/diyan/pywinrm
.. |Coverage Status| image:: https://coveralls.io/repos/diyan/pywinrm/badge.png
:target: https://coveralls.io/r/diyan/pywinrm
Keywords: winrm,ws-man,devops,ws-management
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Clustering
Classifier: Topic :: System :: Distributed Computing
Classifier: Topic :: System :: Systems Administration