/usr/lib/python2.7/dist-packages/txwinrm/SessionManager.py is in python-txwinrm 1.3.3-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | ##############################################################################
#
# Copyright (C) Zenoss, Inc. 2016-2017, all rights reserved.
#
# This content is made available according to terms specified in
# License.zenoss under the directory where your Zenoss product is installed.
#
##############################################################################
"""txsessionmgr - Python module for a single persistent connection to a device
for multiple clients.
Useful for situations when multiple connections to a device can be handled
with one connection through a single login, e.g. txciscoapic, txwinrm
The global SESSION_MANAGER is instantiated one time and is used to manage
all sessions
Session should be subclassed and implemented to login/logout, send requests,
and handle responses
A Client should always have a key property. This will be unique to the types
of transactions/requests being made through a single Session
"""
import copy
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.protocol import Factory
Factory.noisy = False
class Session(object):
"""Session handler for connection to a device.
Session class is responsible for implementing the login/logout methods.
"""
def __init__(self):
# Used to keep track of clients using session.
self._clients = set()
# The currently valid token. This can be anything that the client
# needs to know the connection is alive.
self._token = None
# Deferred waiting for login result.
self._login_d = None
# Error from last login if applicable.
self._login_error = None
# Deferred for logouts
self._logout_dc = None
@inlineCallbacks
def deferred_login(self, client):
"""Kick off a deferred login to a device from the first
client that needs the connection.
Subsequent clients will use the data returned from the first login.
:param client: Client initiating a connection
:type client: ZenPack specific client
:rtype: Deferred
:return: Returns ZenPack unique token to be used for a session.
"""
self._clients.add(client)
if self._token:
returnValue(self._token)
# No one already waiting for a token. Login to get a new one.
if not self._login_d or self._login_d.called:
self._login_d = self._deferred_login(client)
try:
self._token = yield self._login_d
except Exception as e:
self._login_error = e
raise
# At least one other client is already waiting for a token, and
# the login to get it is already in progress. Wait for that
# login to finish, then return its token.
else:
yield self._login_d
if self._login_error:
raise self._login_error
returnValue(self._token)
@inlineCallbacks
def deferred_logout(self):
"""Calls session._deferred_logout() only if all other clients
using the same session have also called deferred_logout.
"""
# we still have clients running, don't logout
if self._clients:
returnValue(None)
if self._token:
try:
# go ahead and clear the token
self._token = None
yield self._deferred_logout()
except Exception:
pass
returnValue(None)
@inlineCallbacks
def _deferred_login(self, client):
"""Performs the ZenPack specific login to a device.
This will only be called from the first client to fire off the deferred.
All other clients will use the _token returned from this method.
:param client: Client initiating a connection
:type client: ZenPack specific client
:rtype: Deferred
:return: Returns a Deferred which is logs into the device.
"""
returnValue(None)
@inlineCallbacks
def _deferred_logout(self, client=None):
"""Performs the ZenPack specific logout from a device.
This will only be called by the last client to logout of the session.
:param client: Client closing connection (Optional)
:type client: ZenPack specific client
:rtype: Deferred
:return: Returns a Deferred which logs out of the device.
"""
returnValue(None)
class SessionManager(object):
"""Class to manage open sessions to devices."""
def __init__(self):
# Used to keep track of sessions.
# a session entry uses a key that is a tuple
# of (ipaddress, some_other_content)
self._sessions = {}
def get_connection(self, key):
"""Return the session for a given key."""
if key is None:
raise Exception('WinRM SessionManager: Client key cannot be empty')
return self._sessions.get(key, None)
def remove_connection(self, key):
"""End a session by a key.
This can happen if the token is too old, the server reboots, or if
the XML API is disabled and enabled.
"""
session = self.get_connection(key)
if session:
self._sessions.pop(key)
@inlineCallbacks
def init_connection(self, client, session_class=Session):
"""Initialize connection to device.
If a session is already started return it
else kick off deferred to initiate session.
The client must contain a key for session storage.
:param client: Client initiating connection
:type client: ZenPack defined client
"""
if not hasattr(client, 'key'):
raise Exception('WinRM SessionManager: Client must contain a key field')
session = self.get_connection(client.key)
if session is not None:
try:
session._logout_dc.cancel()
except Exception:
pass
# add client to set
session._clients.add(client)
# update conn_info in case something changed
session.update_conn_info(client)
# already connected, return
if session._token:
returnValue(session._token)
# no session yet, so create a new one
if session is None:
session = session_class()
self._sessions[client.key] = session
# log in
token = yield session.deferred_login(client)
returnValue(token)
def close_connection(self, client):
"""Kick off a session's logout.
If there are no more clients using a session, remove it.
:param client: Client closing connection
:type client: ZenPack defined class
"""
key = copy.deepcopy(client.key)
session = self.get_connection(key)
if not session:
# should never happen, but check
return
try:
session._logout_dc.cancel()
except Exception:
pass
session._clients.discard(client)
session._logout_dc = reactor.callLater(65, self.deferred_logout, key)
@inlineCallbacks
def deferred_logout(self, key):
# first, get the session from the key
session = self.get_connection(key)
# close current connection and do cleanup for session
yield session._deferred_logout()
returnValue(None)
SESSION_MANAGER = SessionManager()
|