Revision 1d540e60
b/ncclient/glue.py | ||
---|---|---|
15 | 15 |
"TODO: docstring" |
16 | 16 |
|
17 | 17 |
from cStringIO import StringIO |
18 |
from threading import Thread |
|
18 | 19 |
from Queue import Queue |
19 | 20 |
from threading import Lock |
20 | 21 |
from xml.etree import cElementTree as ET |
21 | 22 |
|
23 |
import logging |
|
24 |
logger = logging.getLogger('ncclient.glue') |
|
22 | 25 |
|
23 | 26 |
def parse_root(raw): |
24 | 27 |
'''Parse the top-level element from a string representing an XML document. |
... | ... | |
32 | 35 |
return (element.tag, element.attrib) |
33 | 36 |
|
34 | 37 |
|
35 |
class Subject(object):
|
|
38 |
class Subject(Thread):
|
|
36 | 39 |
|
37 | 40 |
'Meant for subclassing by transport.Session' |
38 | 41 |
|
39 | 42 |
def __init__(self): |
40 | 43 |
"TODO: docstring" |
44 |
Thread.__init__(self) |
|
41 | 45 |
self._q = Queue() |
42 |
self._listeners = set() |
|
46 |
self._listeners = set() # TODO(?) weakref
|
|
43 | 47 |
self._lock = Lock() |
44 | 48 |
|
45 | 49 |
def _dispatch_received(self, raw): |
... | ... | |
48 | 52 |
with self._lock: |
49 | 53 |
listeners = list(self._listeners) |
50 | 54 |
for l in listeners: |
55 |
logger.debug('[dispatching] message to %s' % l) |
|
51 | 56 |
l.callback(root, raw) |
52 | 57 |
|
53 | 58 |
def _dispatch_error(self, err): |
... | ... | |
55 | 60 |
with self._lock: |
56 | 61 |
listeners = list(self._listeners) |
57 | 62 |
for l in listeners: |
63 |
logger.debug('[dispatching] error to %s' % l) |
|
58 | 64 |
l.errback(err) |
59 | 65 |
|
60 | 66 |
def add_listener(self, listener): |
61 | 67 |
"TODO: docstring" |
68 |
logger.debug('[installing listener] %r' % listener) |
|
62 | 69 |
with self._lock: |
63 | 70 |
self._listeners.add(listener) |
64 | 71 |
|
65 | 72 |
def remove_listener(self, listener): |
66 | 73 |
"TODO: docstring" |
74 |
logger.debug('[discarding listener] %r' % listener) |
|
67 | 75 |
with self._lock: |
68 | 76 |
self._listeners.discard(listener) |
69 | 77 |
|
... | ... | |
78 | 86 |
|
79 | 87 |
def send(self, message): |
80 | 88 |
"TODO: docstring" |
81 |
logger.debug('queueing:%s' % message)
|
|
89 |
logger.debug('[queueing] %s' % message)
|
|
82 | 90 |
self._q.put(message) |
83 | 91 |
|
84 | 92 |
|
b/ncclient/operations/rpc.py | ||
---|---|---|
14 | 14 |
|
15 | 15 |
from threading import Event, Lock |
16 | 16 |
from uuid import uuid1 |
17 |
from weakref import WeakValueDictionary |
|
17 | 18 |
|
18 |
from ncclient.content import TreeBuilder, BASE_NS |
|
19 |
from ncclient.content import TreeBuilder |
|
20 |
from ncclient.content import qualify as _ |
|
21 |
from ncclient.content import unqualify as __ |
|
19 | 22 |
from ncclient.glue import Listener |
20 | 23 |
|
21 | 24 |
from . import logger |
... | ... | |
26 | 29 |
|
27 | 30 |
def __init__(self, session, async=False): |
28 | 31 |
self._session = session |
32 |
self._async = async |
|
29 | 33 |
self._id = uuid1().urn |
30 | 34 |
self._listener = RPCReplyListener(session) |
31 | 35 |
self._listener.register(self._id, self) |
... | ... | |
41 | 45 |
def _request(self, op): |
42 | 46 |
req = self._build(op) |
43 | 47 |
self._session.send(req) |
44 |
if async: |
|
48 |
if self._async:
|
|
45 | 49 |
self._reply_event.wait() |
46 | 50 |
self._reply.parse() |
47 | 51 |
return self._reply |
... | ... | |
74 | 78 |
def build_from_spec(msgid, opspec, encoding='utf-8'): |
75 | 79 |
"TODO: docstring" |
76 | 80 |
spec = { |
77 |
'tag': _('rpc', BASE_NS),
|
|
81 |
'tag': _('rpc'), |
|
78 | 82 |
'attributes': {'message-id': msgid}, |
79 | 83 |
'children': opspec |
80 | 84 |
} |
... | ... | |
132 | 136 |
def errback(self, err): |
133 | 137 |
logger.error('RPCReplyListener.errback: %r' % err) |
134 | 138 |
if self._errback is not None: |
135 |
self._errback(err) |
|
139 |
self._errback(err) |
b/ncclient/operations/session.py | ||
---|---|---|
30 | 30 |
self._session.expect_close() |
31 | 31 |
self._session.close() |
32 | 32 |
|
33 |
def request(self, reply_event=None):
|
|
34 |
self._request(self.spec, reply_event)
|
|
33 |
def request(self): |
|
34 |
self._request(self.spec) |
|
35 | 35 |
|
36 | 36 |
|
37 | 37 |
class KillSession(RPC): |
b/ncclient/transport/hello.py | ||
---|---|---|
32 | 32 |
def callback(self, root, raw): |
33 | 33 |
if __(root[0]) == 'hello': |
34 | 34 |
try: |
35 |
id, capabilities = parse(raw) |
|
35 |
id, capabilities = HelloHandler.parse(raw)
|
|
36 | 36 |
except Exception as e: |
37 | 37 |
self._error_cb(e) |
38 | 38 |
else: |
b/ncclient/transport/session.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
from threading import Thread, Event
|
|
15 |
from threading import Event |
|
16 | 16 |
|
17 | 17 |
from ncclient.capabilities import Capabilities, CAPABILITIES |
18 | 18 |
from ncclient.glue import Subject |
... | ... | |
20 | 20 |
from . import logger |
21 | 21 |
from hello import HelloHandler |
22 | 22 |
|
23 |
class Session(Thread, Subject):
|
|
23 |
class Session(Subject): |
|
24 | 24 |
|
25 | 25 |
"TODO: docstring" |
26 | 26 |
|
27 | 27 |
def __init__(self): |
28 | 28 |
"TODO: docstring" |
29 | 29 |
Subject.__init__(self) |
30 |
Thread.__init__(self, name='session')
|
|
31 |
self.setDaemon(True) |
|
30 |
self.setName('session')
|
|
31 |
self.setDaemon(True) #hmm
|
|
32 | 32 |
self._client_capabilities = CAPABILITIES |
33 | 33 |
self._server_capabilities = None # yet |
34 | 34 |
self._id = None # session-id |
35 | 35 |
self._connected = False # to be set/cleared by subclass implementation |
36 |
logger.debug('[session object created] client_capabilities=%r' % |
|
37 |
self._client_capabilities) |
|
36 | 38 |
|
37 | 39 |
def _post_connect(self): |
38 | 40 |
"TODO: docstring" |
39 |
self.send(HelloHandler.build(self._client_capabilities)) |
|
40 |
error = None |
|
41 | 41 |
init_event = Event() |
42 |
error = [None] # so that err_cb can bind error[0]. just how it is. |
|
42 | 43 |
# callbacks |
43 | 44 |
def ok_cb(id, capabilities): |
44 |
self._id, self._server_capabilities = id, Capabilities(capabilities) |
|
45 |
self._id = id |
|
46 |
self._server_capabilities = Capabilities(capabilities) |
|
45 | 47 |
init_event.set() |
46 | 48 |
def err_cb(err): |
47 |
error = err |
|
49 |
error[0] = err
|
|
48 | 50 |
init_event.set() |
49 | 51 |
listener = HelloHandler(ok_cb, err_cb) |
50 | 52 |
self.add_listener(listener) |
51 |
# start the subclass' main loop |
|
53 |
self.send(HelloHandler.build(self._client_capabilities)) |
|
54 |
logger.debug('[starting main loop]') |
|
52 | 55 |
self.start() |
53 | 56 |
# we expect server's hello message |
54 | 57 |
init_event.wait() |
55 | 58 |
# received hello message or an error happened |
56 | 59 |
self.remove_listener(listener) |
57 |
if error: |
|
58 |
raise error |
|
60 |
if error[0]:
|
|
61 |
raise error[0]
|
|
59 | 62 |
logger.info('initialized: session-id=%s | server_capabilities=%s' % |
60 |
(self.id, self.server_capabilities))
|
|
63 |
(self._id, self._server_capabilities))
|
|
61 | 64 |
|
62 | 65 |
def connect(self, *args, **kwds): |
63 | 66 |
"TODO: docstring" |
b/ncclient/transport/ssh.py | ||
---|---|---|
37 | 37 |
self._transport = None |
38 | 38 |
self._connected = False |
39 | 39 |
self._channel = None |
40 |
self._expecting_close = False |
|
40 | 41 |
self._buffer = StringIO() # for incoming data |
41 | 42 |
# parsing-related, see _parse() |
42 | 43 |
self._parsing_state = 0 |
43 | 44 |
self._parsing_pos = 0 |
45 |
logger.debug('[SSHSession object created]') |
|
44 | 46 |
|
45 | 47 |
def _parse(self): |
46 | 48 |
'''Messages ae delimited by MSG_DELIM. The buffer could have grown by a |
... | ... | |
85 | 87 |
self._parsing_state = expect |
86 | 88 |
self._parsing_pos = self._buffer.tell() |
87 | 89 |
|
90 |
def expect_close(self): |
|
91 |
self._expecting_close = True |
|
92 |
|
|
88 | 93 |
def load_system_host_keys(self, filename=None): |
89 | 94 |
if filename is None: |
90 | 95 |
filename = os.path.expanduser('~/.ssh/known_hosts') |
... | ... | |
266 | 271 |
raise SessionCloseError(self._buffer.getvalue(), data) |
267 | 272 |
data = data[n:] |
268 | 273 |
except Exception as e: |
269 |
self.close() |
|
270 | 274 |
logger.debug('*** broke out of main loop ***') |
271 |
self._dispatch_error(e) |
|
275 |
self.close() |
|
276 |
if not (isinstance(e, SessionCloseError) and self._expecting_close): |
|
277 |
self._dispatch_error(e) |
|
272 | 278 |
|
273 | 279 |
@property |
274 | 280 |
def transport(self): |
b/ncclient/transport/util.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
from . import logger
|
|
15 |
from ncclient.glue import Listener
|
|
16 | 16 |
|
17 |
class DebugListener: |
|
17 |
import logging |
|
18 |
logger = logging.getLogger('DebugListener') |
|
19 |
|
|
20 |
class DebugListener(Listener): |
|
18 | 21 |
|
19 | 22 |
def __str__(self): |
20 | 23 |
return 'DebugListener' |
21 | 24 |
|
22 | 25 |
def received(self, raw): |
23 |
logger.debug('DebugListener:[received]:||%s||' % raw)
|
|
26 |
logger.debug('[received]:||%s||' % raw) |
|
24 | 27 |
|
25 |
def error(self, err): |
|
26 |
logger.debug('DebugListener:[error]:%r' % err) |
|
28 |
def errback(self, err): |
|
29 |
logger.debug('[error]:%r' % err) |
Also available in: Unified diff