pywinrm-0.0.3/0000775000175000017500000000000012405714354013460 5ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/0000775000175000017500000000000012405714354014614 5ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/protocol.py0000664000175000017500000003615712405700664017042 0ustar diyandiyan00000000000000import 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/0000775000175000017500000000000012405714354015756 5ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/conftest.py0000664000175000017500000005615612405704532020167 0ustar diyandiyan00000000000000import 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.py0000664000175000017500000000252012405700664021050 0ustar diyandiyan00000000000000from 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.py0000664000175000017500000000000012405700664021540 0ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/test_cmd.py0000664000175000017500000000000012405700664020117 0ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/test_integration_protocol.py0000664000175000017500000000422212405700664023632 0ustar diyandiyan00000000000000import 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.py0000664000175000017500000000000012405700664023074 0ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/__init__.py0000664000175000017500000000000012405700664020054 0ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/test_wql.py0000664000175000017500000000000012405700664020157 0ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/test_protocol.py0000664000175000017500000000250612405700664021232 0ustar diyandiyan00000000000000def 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.ps10000664000175000017500000000000012405700664021235 0ustar diyandiyan00000000000000pywinrm-0.0.3/winrm/tests/test_integration_session.py0000664000175000017500000000020012405700664023444 0ustar diyandiyan00000000000000import pytest from winrm import Session xfail = pytest.mark.xfail @xfail() def test_run_cmd(): raise NotImplementedError()pywinrm-0.0.3/winrm/transport.py0000664000175000017500000002350512405704532017224 0ustar diyandiyan00000000000000import 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__.py0000664000175000017500000001043112405700664016723 0ustar diyandiyan00000000000000import 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.py0000664000175000017500000000170712405700664017353 0ustar diyandiyan00000000000000import 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): passpywinrm-0.0.3/setup.py0000664000175000017500000000325412405713372015175 0ustar diyandiyan00000000000000from 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-INFO0000664000175000017500000001472712405714354014570 0ustar diyandiyan00000000000000Metadata-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