[Python-modules-commits] [txwinrm] 01/09: Import txwinrm_1.2.20.orig.tar.gz

Christopher Stuart Hoskin mans0954 at moszumanska.debian.org
Mon Jun 26 01:47:00 UTC 2017


This is an automated email from the git hooks/post-receive script.

mans0954 pushed a commit to branch master
in repository txwinrm.

commit 725ae9c1a9a2e5be561af17bc361181305870603
Author: Christopher Hoskin <mans0954 at debian.org>
Date:   Mon Jun 26 02:23:36 2017 +0100

    Import txwinrm_1.2.20.orig.tar.gz
---
 setup.py                  |   2 +-
 txwinrm/SessionManager.py |   6 +-
 txwinrm/WinRMClient.py    | 148 +++++++++++++++++++++-------------------------
 txwinrm/app.py            |   4 +-
 txwinrm/krb5.py           |  42 ++++++++-----
 txwinrm/shell.py          | 108 +++++++++++++++++++++++++++------
 txwinrm/util.py           |  60 ++++++++++++-------
 7 files changed, 231 insertions(+), 139 deletions(-)

diff --git a/setup.py b/setup.py
index b0b2ba7..28b475e 100755
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@
 
 setup_kwargs = dict(
     name='txwinrm',
-    version='1.2.7',
+    version='1.2.20',
     description='Asynchronous Python WinRM client',
     long_description=open('README.rst').read(),
     license='See LICENSE file',
diff --git a/txwinrm/SessionManager.py b/txwinrm/SessionManager.py
index c03508a..e60dc28 100755
--- a/txwinrm/SessionManager.py
+++ b/txwinrm/SessionManager.py
@@ -98,8 +98,7 @@ class Session(object):
 
             self._token = None
 
-        if client in self._clients:
-            self._clients.remove(client)
+        self._clients.discard(client)
         returnValue(None)
 
     @inlineCallbacks
@@ -171,8 +170,7 @@ class SessionManager(object):
         session = self.get_connection(client.key)
         if session:
             if session._token:
-                if client not in session._clients:
-                    session._clients.add(client)
+                session._clients.add(client)
                 returnValue(session._token)
 
         if session is None:
diff --git a/txwinrm/WinRMClient.py b/txwinrm/WinRMClient.py
index 2cfac59..216cc42 100755
--- a/txwinrm/WinRMClient.py
+++ b/txwinrm/WinRMClient.py
@@ -7,6 +7,7 @@
 #
 ##############################################################################
 
+import copy
 import logging
 from collections import namedtuple
 from httplib import BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, OK
@@ -51,12 +52,13 @@ from .util import (
 from .shell import (
     _find_shell_id,
     _build_command_line_elem,
+    _build_ps_command_line_elem,
     _find_command_id,
     _MAX_REQUESTS_PER_COMMAND,
     _find_stream,
     _find_exit_code,
     CommandResponse,
-    _stripped_lines
+    _stripped_lines,
 )
 from .enumerate import (
     DEFAULT_RESOURCE_URI,
@@ -67,48 +69,6 @@ from .SessionManager import SESSION_MANAGER, Session
 kerberos = None
 LOG = logging.getLogger('winrm')
 
-EnumInfo = namedtuple('EnumInfo', ['wql', 'resource_uri'])
-
-
-def _build_ps_command_line_elem(ps_command, ps_script):
-    """Build PowerShell command line elements without splitting
-    the actual ps script into arguments. using _build_command_line_elem
-    with a ps script splits the script into separate arguments.  Remote
-    Windows shell inserts spaces when reconstituting the script.
-
-    ps_command - powershell command with arguments as string
-        e.g. 'powershell -NoLogo -NonInteractive -NoProfile -Command'
-    ps_script - script to be run in powershell as single line string
-        e.g. "& {get-counter -counter \"\memory\pages output/sec\" }"
-    """
-    command_line_parts = shlex.split(ps_command, posix=False)
-    # ensure '-command' is last
-    if command_line_parts[-1:][0].lower() != '-command':
-        index = 0
-        for option in command_line_parts:
-            if option.lower() == '-command':
-                command_line_parts.pop(index)
-                break
-            index += 1
-        command_line_parts.append(option)
-    prefix = "rsp"
-    ET.register_namespace(prefix, c.XML_NS_MSRSP)
-    command_line_elem = ET.Element('{%s}CommandLine' % c.XML_NS_MSRSP)
-    command_elem = ET.Element('{%s}Command' % c.XML_NS_MSRSP)
-    command_elem.text = command_line_parts[0]
-    command_line_elem.append(command_elem)
-    for arguments_text in command_line_parts[1:]:
-        arguments_elem = ET.Element('{%s}Arguments' % c.XML_NS_MSRSP)
-        arguments_elem.text = arguments_text
-        command_line_elem.append(arguments_elem)
-    arguments_elem = ET.Element('{%s}Arguments' % c.XML_NS_MSRSP)
-    arguments_elem.text = ps_script
-    command_line_elem.append(arguments_elem)
-    tree = ET.ElementTree(command_line_elem)
-    str_io = StringIO()
-    tree.write(str_io, encoding='utf-8')
-    return str_io.getvalue()
-
 
 class WinRMSession(Session):
     '''
@@ -250,7 +210,7 @@ class WinRMSession(Session):
                 import xml.dom.minidom
                 xml = xml.dom.minidom.parseString(xml_str)
                 LOG.debug(xml.toprettyxml())
-            except:
+            except Exception:
                 LOG.debug('Could not prettify response XML: "{0}"'.format(xml_str))
         returnValue(ET.fromstring(xml_str))
 
@@ -305,49 +265,62 @@ class WinRMClient(object):
         verify_conn_info(conn_info)
         self.key = None
         self._conn_info = conn_info
-        self.session_manager = SESSION_MANAGER
-        self._session = None
         self.ps_script = None
 
+    def _session(self):
+        return SESSION_MANAGER.get_connection(self.key)
+
     @inlineCallbacks
     def init_connection(self):
-        '''Initialize a connection through the session_manager'''
-        yield self.session_manager.init_connection(self, WinRMSession)
-        self._session = self.session_manager.get_connection(self.key)
+        """Initialize a connection through the session_manager"""
+        if self._session() is not None:
+            try:
+                self._refresh_dc.cancel()
+            except Exception:
+                pass
+        yield SESSION_MANAGER.init_connection(self, WinRMSession)
         returnValue(None)
 
     def is_kerberos(self):
         return self._conn_info.auth_type == 'kerberos'
 
     def decrypt_body(self, body):
-        return self._session.decrypt_body(body)
+        return self._session().decrypt_body(body)
+
+    @inlineCallbacks
+    def send_request(self, request, **kwargs):
+        if not self._session():
+            yield self.init_connection()
+
+        if not self._session():
+            raise Exception('Could not connect to device {}'.format(self.conn_info.hostname))
+        response = yield self._session().send_request(request, self, **kwargs)
+        returnValue(response)
 
     @inlineCallbacks
     def _create_shell(self):
-        elem = yield self._session.send_request('create', self)
+        elem = yield self.send_request('create')
         returnValue(_find_shell_id(elem))
 
     @inlineCallbacks
     def _delete_shell(self, shell_id):
-        yield self._session.send_request('delete', self, shell_id=shell_id)
+        yield self.send_request('delete', shell_id=shell_id)
         returnValue(None)
 
     @inlineCallbacks
     def _signal_terminate(self, shell_id, command_id):
-        yield self._session.send_request('signal',
-                                         self,
-                                         shell_id=shell_id,
-                                         command_id=command_id,
-                                         signal_code=c.SHELL_SIGNAL_TERMINATE)
+        yield self.send_request('signal',
+                                shell_id=shell_id,
+                                command_id=command_id,
+                                signal_code=c.SHELL_SIGNAL_TERMINATE)
         returnValue(None)
 
     @inlineCallbacks
     def _signal_ctrl_c(self, shell_id, command_id):
-        yield self._session.send_request('signal',
-                                         self,
-                                         shell_id=shell_id,
-                                         command_id=command_id,
-                                         signal_code=c.SHELL_SIGNAL_CTRL_C)
+        yield self.send_request('signal',
+                                shell_id=shell_id,
+                                command_id=command_id,
+                                signal_code=c.SHELL_SIGNAL_CTRL_C)
         returnValue(None)
 
     @inlineCallbacks
@@ -360,20 +333,20 @@ class WinRMClient(object):
         LOG.debug('WinRMClient._send_command: sending command request '
                   '(shell_id={0}, command_line_elem={1})'.format(
                       shell_id, command_line_elem))
-        command_elem = yield self._session.send_request(
-            'command', self, shell_id=shell_id, command_line_elem=command_line_elem,
+        command_elem = yield self.send_request(
+            'command', shell_id=shell_id, command_line_elem=command_line_elem,
             timeout=self._conn_info.timeout)
         returnValue(command_elem)
 
     @inlineCallbacks
     def _send_receive(self, shell_id, command_id):
-        receive_elem = yield self._session.send_request(
-            'receive', self, shell_id=shell_id, command_id=command_id)
+        receive_elem = yield self.send_request(
+            'receive', shell_id=shell_id, command_id=command_id)
         returnValue(receive_elem)
 
     @inlineCallbacks
     def close_connection(self):
-        yield self._session.close_connection(self)
+        yield self._session().close_connection(self)
         returnValue(None)
 
 
@@ -397,8 +370,8 @@ class SingleCommandClient(WinRMClient):
         self.ps_script = ps_script
         yield self.init_connection()
         try:
-            cmd_response = yield self._session.sem.run(self.run_single_command,
-                                                       command_line)
+            cmd_response = yield self._session().sem.run(self.run_single_command,
+                                                         command_line)
         except Exception:
             yield self.close_connection()
             raise
@@ -449,7 +422,14 @@ class SingleCommandClient(WinRMClient):
 
 
 class LongCommandClient(WinRMClient):
-    """Client to run a single long running command to a winrm device"""
+    """UNDER CONSTRUCTION!!! DO NOT USE!!
+
+    Having issues with this client.  May Need to be reworked a bit
+
+    TODO: If raising exception, we need to delete the remote shell
+    before closing connection
+
+    Client to run a single long running command to a winrm device"""
     def __init__(self, conn_info):
         super(LongCommandClient, self).__init__(conn_info)
         self._shell_id = None
@@ -470,11 +450,13 @@ class LongCommandClient(WinRMClient):
         self.key = (self._conn_info.ipaddress, command_line + str(ps_script))
         self.ps_script = ps_script
         yield self.init_connection()
-        self._shell_id = yield self._create_shell()
+        if self._shell_id is None:
+            self._shell_id = yield self._create_shell()
         try:
             command_elem = yield self._send_command(self._shell_id,
                                                     command_line)
         except TimeoutError:
+            yield self._sender.send_request('delete', shell_id=self._shell_id)
             yield self.close_connection()
             raise
         self._command_id = _find_command_id(command_elem)
@@ -524,7 +506,8 @@ class EnumerateClient(WinRMClient):
     @inlineCallbacks
     def enumerate(self, wql, resource_uri=DEFAULT_RESOURCE_URI):
         """Runs a remote WQL query."""
-        yield self.init_connection()
+        if self._session() is None:
+            yield self.init_connection()
         request_template_name = 'enumerate'
         enumeration_context = None
         items = []
@@ -532,7 +515,7 @@ class EnumerateClient(WinRMClient):
             for i in xrange(_MAX_REQUESTS_PER_ENUMERATION):
                 LOG.info('{0} "{1}" {2}'.format(
                     self._hostname, wql, request_template_name))
-                response = yield self._session._send_request(
+                response = yield self._session()._send_request(
                     request_template_name,
                     self,
                     resource_uri=resource_uri,
@@ -564,12 +547,11 @@ class EnumerateClient(WinRMClient):
         """
         items = {}
         yield self.init_connection()
-        self._session = self.session_manager.get_connection(self.key)
         for enum_info in enum_infos:
             try:
-                items[enum_info] = yield self._session.sem.run(self.enumerate,
-                                                               enum_info.wql,
-                                                               enum_info.resource_uri)
+                items[enum_info] = yield self._session().sem.run(self.enumerate,
+                                                                 enum_info.wql,
+                                                                 enum_info.resource_uri)
             except (UnauthorizedError, ForbiddenError):
                 # Fail the collection for general errors.
                 raise
@@ -628,17 +610,23 @@ class AssociatorClient(EnumerateClient):
 
         see https://msdn.microsoft.com/en-us/library/aa384793(v=vs.85).aspx
         """
+        associations_copy = copy.deepcopy(associations)
         items = {}
         wql = 'Select {} from {}'.format(','.join(fields), seed_class)
         if where:
             wql += ' where {}'.format(where)
+        EnumInfo = namedtuple('EnumInfo', ['wql', 'resource_uri'])
         enum_info = EnumInfo(wql, resource_uri)
         results = yield self.do_collect([enum_info])
-        input_results = results[enum_info]
+
+        try:
+            input_results = results[enum_info]
+        except KeyError:
+            raise Exception('No results for seed class {}.'.format(seed_class))
 
         items[seed_class] = input_results
-        while associations:
-            association = associations.pop(0)
+        while associations_copy:
+            association = associations_copy.pop(0)
             associate_results = []
             prop_results = {}
             for item in input_results:
diff --git a/txwinrm/app.py b/txwinrm/app.py
index 5a93753..2feda3c 100644
--- a/txwinrm/app.py
+++ b/txwinrm/app.py
@@ -221,6 +221,7 @@ def _parse_args(utility):
     parser.add_argument("--ipaddress", "-s")
     parser.add_argument("--service", "-e", help='http/https/wsman', default='http')
     parser.add_argument("--includedir", help="valid includedir")
+    parser.add_argument("--disable_rdns", action="store_true", help="disable kerberos reverse lookups")
     utility.add_args(parser)
     args = parser.parse_args()
     if not args.config:
@@ -236,12 +237,13 @@ def _parse_args(utility):
             if not password:
                 password = getpass()
             connectiontype = 'Keep-Alive'
+            disable_rdns = True if args.disable_rdns else False
             args.conn_info = ConnectionInfo(
                 hostname, args.authentication,
                 args.username, password, scheme,
                 port, connectiontype, args.keytab,
                 args.dcip, ipaddress=args.ipaddress, service=args.service,
-                include_dir=args.includedir)
+                include_dir=args.includedir, disable_rdns=disable_rdns)
             try:
                 verify_conn_info(args.conn_info)
             except Exception as e:
diff --git a/txwinrm/krb5.py b/txwinrm/krb5.py
index eed3bf3..1a733fe 100644
--- a/txwinrm/krb5.py
+++ b/txwinrm/krb5.py
@@ -26,7 +26,7 @@ __all__ = [
 
 KRB5_CONF_TEMPLATE = (
     "# This file is managed by the txwinrm python module.\n"
-    "# NOTE: Any changes to the logging, libdefaults, domain_realm"
+    "# NOTE: Any changes to the logging, libdefaults, domain_realm\n"
     "# sections of this file will be overwritten.\n"
     "#\n"
     "\n"
@@ -43,6 +43,7 @@ KRB5_CONF_TEMPLATE = (
     " ticket_lifetime = 24h\n"
     " renew_lifetime = 7d\n"
     " forwardable = true\n"
+    "{disable_rdns}"
     "\n"
     "[realms]\n"
     "{realms_text}"
@@ -98,6 +99,7 @@ class Config(object):
         """Initialize instance with data from KRB5_CONFIG."""
         self.path = self.get_path()
         self.includedirs = set()
+        self.disable_rdns = False
         self.realms, self.admin_servers = self.load()
 
         # For further usage by kerberos python module.
@@ -131,7 +133,7 @@ class Config(object):
             self.save()
         defer.returnValue(None)
 
-    def add_kdc(self, realm, kdcs):
+    def add_kdc(self, realm, kdcs, disable_rdns=False):
         """Add realm and KDC to KRB5_CONFIG.
         Allow for comma separated string of kdcs with regex
         Use + or nothing to add, - to remove, * for admin_server
@@ -167,10 +169,13 @@ class Config(object):
 
         new_kdcs = self.realms[realm].symmetric_difference(set(valid_kdcs))
         bad_kdcs = self.realms[realm].intersection(set(remove_kdcs))
-        if not new_kdcs and not bad_kdcs and admin_server == self.admin_servers[realm]:
+        if (not new_kdcs and not bad_kdcs and
+                admin_server == self.admin_servers[realm] and
+                self.disable_rdns == disable_rdns):
             # nothing to do
             return
 
+        self.disable_rdns = disable_rdns
         self.realms[realm] = self.realms[realm].union(new_kdcs) - bad_kdcs
         self.admin_servers[realm] = admin_server
         self.save()
@@ -237,6 +242,11 @@ class Config(object):
                     match = re.search(r'includedir (\S+)', line)
                     if match:
                         self.includedirs.add(match.group(1))
+                elif line.strip().startswith('rdns'):
+                    try:
+                        self.disable_rdns = True if line.split('=')[1].strip() == 'false' else False
+                    except Exception:
+                        self.disable_rdns = False
                 elif in_realms_section:
                     line = line.strip()
                     if not line:
@@ -297,13 +307,17 @@ class Config(object):
                 INCLUDEDIR_TEMPLATE.format(
                     includedir=includedir))
 
-        with open(self.path, 'w') as krb5_conf:
-            krb5_conf.write(
-                KRB5_CONF_TEMPLATE.format(
-                    includedir=''.join(includedir_list),
-                    realms_text=''.join(realms_list),
-                    domain_realm_text=''.join(domain_realm_list)))
-
+        disable_rdns = ' rdns = false\n' if self.disable_rdns else ''
+        try:
+            with open(self.path, 'w') as krb5_conf:
+                krb5_conf.write(
+                    KRB5_CONF_TEMPLATE.format(
+                        disable_rdns=disable_rdns,
+                        includedir=''.join(includedir_list),
+                        realms_text=''.join(realms_list),
+                        domain_realm_text=''.join(domain_realm_list)))
+        except IOError as e:
+            pass
 
 # Singleton. Loads from KRB5_CONFIG on import.
 config = Config()
@@ -340,7 +354,7 @@ class KinitProcessProtocol(ProcessProtocol):
 
 
 @defer.inlineCallbacks
-def kinit(username, password, kdc, includedir=None):
+def kinit(username, password, kdc, includedir=None, disable_rdns=False):
     """Perform kerberos initialization."""
     kinit = None
     for path in ('/usr/bin/kinit', '/usr/kerberos/bin/kinit'):
@@ -362,7 +376,7 @@ def kinit(username, password, kdc, includedir=None):
 
     if includedir:
         yield config.add_includedir(includedir)
-    config.add_kdc(realm, kdc)
+    config.add_kdc(realm, kdc, disable_rdns)
 
     ccname = config.get_ccname(username)
     dirname = os.path.dirname(ccname)
@@ -388,8 +402,8 @@ def ccname(username):
     return config.get_ccname(username)
 
 
-def add_trusted_realm(realm, kdc):
+def add_trusted_realm(realm, kdc, disable_rdns=False):
     """Add a trusted realm for cross realm authentication"""
     trusted_realm = realm.upper()
     global config
-    config.add_kdc(trusted_realm, kdc)
+    config.add_kdc(trusted_realm, kdc, disable_rdns)
diff --git a/txwinrm/shell.py b/txwinrm/shell.py
index 0b4b1fb..f497499 100644
--- a/txwinrm/shell.py
+++ b/txwinrm/shell.py
@@ -19,10 +19,11 @@ from twisted.internet import reactor, defer, task
 from twisted.internet.error import TimeoutError
 from xml.etree import cElementTree as ET
 from . import constants as c
-from .util import create_etree_request_sender, get_datetime
+from .util import create_etree_request_sender, get_datetime, RequestError
 
 log = logging.getLogger('winrm')
 _MAX_REQUESTS_PER_COMMAND = 9999
+_MAX_RETRIES = 3
 
 
 class CommandResponse(object):
@@ -49,6 +50,46 @@ class CommandResponse(object):
             stdout=self.stdout, stderr=self.stderr, exit_code=self.exit_code))
 
 
+def _build_ps_command_line_elem(ps_command, ps_script):
+    """Build PowerShell command line elements without splitting
+    the actual ps script into arguments. using _build_command_line_elem
+    with a ps script splits the script into separate arguments.  Remote
+    Windows shell inserts spaces when reconstituting the script.
+
+    ps_command - powershell command with arguments as string
+        e.g. 'powershell -NoLogo -NonInteractive -NoProfile -Command'
+    ps_script - script to be run in powershell as single line string
+        e.g. "& {get-counter -counter \"\memory\pages output/sec\" }"
+    """
+    command_line_parts = shlex.split(ps_command, posix=False)
+    # ensure '-command' is last
+    if command_line_parts[-1:][0].lower() != '-command':
+        index = 0
+        for option in command_line_parts:
+            if option.lower() == '-command':
+                command_line_parts.pop(index)
+                break
+            index += 1
+        command_line_parts.append(option)
+    prefix = "rsp"
+    ET.register_namespace(prefix, c.XML_NS_MSRSP)
+    command_line_elem = ET.Element('{%s}CommandLine' % c.XML_NS_MSRSP)
+    command_elem = ET.Element('{%s}Command' % c.XML_NS_MSRSP)
+    command_elem.text = command_line_parts[0]
+    command_line_elem.append(command_elem)
+    for arguments_text in command_line_parts[1:]:
+        arguments_elem = ET.Element('{%s}Arguments' % c.XML_NS_MSRSP)
+        arguments_elem.text = arguments_text
+        command_line_elem.append(arguments_elem)
+    arguments_elem = ET.Element('{%s}Arguments' % c.XML_NS_MSRSP)
+    arguments_elem.text = ps_script
+    command_line_elem.append(arguments_elem)
+    tree = ET.ElementTree(command_line_elem)
+    str_io = StringIO()
+    tree.write(str_io, encoding='utf-8')
+    return str_io.getvalue()
+
+
 def _build_command_line_elem(command_line):
     command_line_parts = shlex.split(command_line, posix=False)
     prefix = "rsp"
@@ -182,11 +223,15 @@ class LongRunningCommand(object):
         self._exit_code = None
 
     @defer.inlineCallbacks
-    def start(self, command_line):
+    def start(self, command_line, ps_script=None):
         log.debug("LongRunningCommand run_command: {0}".format(command_line))
-        elem = yield self._sender.send_request('create')
-        self._shell_id = _find_shell_id(elem)
-        command_line_elem = _build_command_line_elem(command_line)
+        if self._shell_id is None:
+            elem = yield self._sender.send_request('create')
+            self._shell_id = _find_shell_id(elem)
+        if ps_script is not None:
+            command_line_elem = _build_ps_command_line_elem(command_line, ps_script)
+        else:
+            command_line_elem = _build_command_line_elem(command_line)
         log.debug('LongRunningCommand run_command: sending command request '
                   '(shell_id={0}, command_line_elem={1})'.format(
                     self._shell_id, command_line_elem))
@@ -196,7 +241,7 @@ class LongRunningCommand(object):
                 command_line_elem=command_line_elem,
                 timeout=self._sender._sender._conn_info.timeout)
         except TimeoutError:
-            yield self._sender.close_connections()
+            yield self.delete_and_close()
             raise
         self._command_id = _find_command_id(command_elem)
 
@@ -208,7 +253,7 @@ class LongRunningCommand(object):
                 shell_id=self._shell_id,
                 command_id=self._command_id)
         except TimeoutError:
-            yield self._sender.close_connections()
+            yield self.delete_and_close()
             raise
         stdout_parts = _find_stream(receive_elem, self._command_id, 'stdout')
         stderr_parts = _find_stream(receive_elem, self._command_id, 'stderr')
@@ -218,23 +263,48 @@ class LongRunningCommand(object):
         defer.returnValue((stdout, stderr))
 
     @defer.inlineCallbacks
+    def delete_and_close(self):
+        """Delete the remote shell and close connection"""
+        yield self._sender.send_request('delete', shell_id=self._shell_id)
+        self._shell_id = None
+        yield self._sender.close_connections()
+        defer.returnValue(None)
+
+    @defer.inlineCallbacks
     def stop(self):
-        yield self._sender.send_request(
-            'signal',
-            shell_id=self._shell_id,
-            command_id=self._command_id,
-            signal_code=c.SHELL_SIGNAL_CTRL_C)
+        for _ in xrange(_MAX_RETRIES):
+            try:
+                yield self._sender.send_request(
+                    'signal',
+                    shell_id=self._shell_id,
+                    command_id=self._command_id,
+                    signal_code=c.SHELL_SIGNAL_CTRL_C)
+            except RequestError as e:
+                # if we get a 500 error back, let's try again
+                if 'HTTP status: 500. An internal error occurred' in e.message:
+                    continue
+                else:
+                    yield self.delete_and_close()
+                    raise e
+            except Exception:
+                yield self.delete_and_close()
+                raise
+            else:
+                break
         try:
             stdout, stderr = yield self.receive()
         except TimeoutError:
-            # close_connections done in receive()
+            # close_connections done in receive() for TimeoutError
+            raise
+        try:
+            yield self._sender.send_request(
+                'signal',
+                shell_id=self._shell_id,
+                command_id=self._command_id,
+                signal_code=c.SHELL_SIGNAL_TERMINATE)
+        except RequestError:
             pass
-        yield self._sender.send_request(
-            'signal',
-            shell_id=self._shell_id,
-            command_id=self._command_id,
-            signal_code=c.SHELL_SIGNAL_TERMINATE)
-        yield self._sender.send_request('delete', shell_id=self._shell_id)
+        yield self.delete_and_close()
         defer.returnValue(CommandResponse(stdout, stderr, self._exit_code))
 
 
diff --git a/txwinrm/util.py b/txwinrm/util.py
index 79a8694..417a5f9 100755
--- a/txwinrm/util.py
+++ b/txwinrm/util.py
@@ -19,6 +19,7 @@ from xml.etree.ElementTree import ParseError
 from twisted.internet import reactor, defer
 from twisted.internet.protocol import Protocol
 from twisted.web.client import Agent
+from twisted.internet.error import ConnectError
 from twisted.internet.ssl import ClientContextFactory
 from twisted.web.http_headers import Headers
 from twisted.internet.threads import deferToThread
@@ -149,7 +150,11 @@ class _ErrorReader(Protocol):
 
     def connectionLost(self, reason):
         if self.gssclient:
-            body = self.gssclient.decrypt_body(''.join(self._data))
+            try:
+                body = self.gssclient.decrypt_body(''.join(self._data))
+            except Exception as e:
+                body = 'There was a problem decrypting an error message: {}.'\
+                       ' Check WinRM logs on {}'.format(e.message, self.gssclient._conn_info.hostname)
         else:
             body = ''.join(self._data)
         message = _parse_error_message(body)
@@ -265,7 +270,8 @@ class AuthGSSClient(object):
                 kinit_result = yield kinit(self._username,
                                            self._password,
                                            self._dcip,
-                                           includedir=self._include_dir)
+                                           includedir=self._include_dir,
+                                           disable_rdns=self._conn_info.disable_rdns)
                 if kinit_result:
                     # this error is ok.  it just means more
                     # than one process is calling kinit
@@ -432,11 +438,12 @@ class ConnectionInfo(namedtuple(
         'envelope_size',
         'code_page',
         'locale',
-        'include_dir'])):
+        'include_dir',
+        'disable_rdns'])):
     def __new__(cls, hostname, auth_type, username, password, scheme, port,
                 connectiontype, keytab, dcip, timeout=60, trusted_realm='',
                 trusted_kdc='', ipaddress='', service='', envelope_size=512000,
-                code_page=65001, locale='en-US', include_dir=None):
+                code_page=65001, locale='en-US', include_dir=None, disable_rdns=False):
         if not ipaddress:
             ipaddress = hostname
         if not service:
@@ -448,7 +455,7 @@ class ConnectionInfo(namedtuple(
                                                   trusted_realm, trusted_kdc,
                                                   ipaddress, service,
                                                   envelope_size, code_page, locale,
-                                                  include_dir)
+                                                  include_dir, disable_rdns)
 
 
 def verify_include_dir(conn_info):
@@ -614,30 +621,43 @@ class RequestSender(object):
             body_producer = _StringProducer(encrypted_request)
         else:
             body_producer = _StringProducer(request)
+
+        @defer.inlineCallbacks
+        def reset_agent_resend(sender, request, body_producer):
+            sender.agent = _get_agent()
+            if sender.gssclient is not None:
+                # do some cleanup first.  memory leaks were occurring
+                sender.gssclient.cleanup()
+                sender.gssclient = None
+                try:
+                    yield sender._set_url_and_headers()
+                    encrypted_request = sender.gssclient.encrypt_body(request)
+                    if not encrypted_request.startswith("--Encrypted Boundary"):
+                        sender._headers.setRawHeaders('Content-Type', _CONTENT_TYPE['Content-Type'])
+                    body_producer = _StringProducer(encrypted_request)
+                except Exception as e:
+                    raise e
+            try:
+                response = yield sender.agent.request(
+                    'POST', sender._url, sender._headers, body_producer)
+            except Exception as e:
+                raise e
+            defer.returnValue(response)
+
         try:
             response = yield self.agent.request(
                 'POST', self._url, self._headers, body_producer)
+        except ConnectError:
+            # network timeout.  could be stale connection, so let's reset
+            response = yield reset_agent_resend(self, request, body_producer)
         except Exception as e:
             raise e
+
         log.debug('received response {0} {1}'.format(
             response.code, request_template_name))
         if response.code == httplib.UNAUTHORIZED or response.code == httplib.BAD_REQUEST:
             # check to see if we need to re-authorize due to lost connection or bad request error
-            if self.gssclient is not None:
-                self.agent = _get_agent()
-                # do some cleanup first.  memory leaks were occurring
-                self.gssclient.cleanup()
-                self.gssclient = None
-                try:
-                    yield self._set_url_and_headers()
-                    encrypted_request = self.gssclient.encrypt_body(request)
-                    if not encrypted_request.startswith("--Encrypted Boundary"):
-                        self._headers.setRawHeaders('Content-Type', _CONTENT_TYPE['Content-Type'])
-                    body_producer = _StringProducer(encrypted_request)
-                    response = yield self.agent.request(
-                        'POST', self._url, self._headers, body_producer)
-                except Exception as e:
-                    raise e
+            response = yield reset_agent_resend(self, request, body_producer)
             if response.code == httplib.UNAUTHORIZED:
                 if self.is_kerberos():
                     auth_header = response.headers.getRawHeaders('WWW-Authenticate')[0]

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/txwinrm.git



More information about the Python-modules-commits mailing list