Revision 66423c8e ncclient/session/ssh.py
b/ncclient/session/ssh.py | ||
---|---|---|
14 | 14 |
|
15 | 15 |
from cStringIO import StringIO |
16 | 16 |
from os import SEEK_CUR |
17 |
import socket
|
|
17 |
from select import select
|
|
18 | 18 |
|
19 | 19 |
import paramiko |
20 | 20 |
|
... | ... | |
24 | 24 |
BUF_SIZE = 4096 |
25 | 25 |
MSG_DELIM = ']]>]]>' |
26 | 26 |
|
27 |
# TODO: |
|
28 |
# chuck SSHClient and use paramiko low-level api to get cisco compatibility |
|
29 |
# and finer control over host key verification, authentication, and error |
|
30 |
# handling |
|
31 |
|
|
27 | 32 |
class SSHSession(Session): |
28 | 33 |
|
29 | 34 |
def __init__(self, load_known_hosts=True, |
... | ... | |
43 | 48 |
self._connected = False |
44 | 49 |
|
45 | 50 |
def _fresh_data(self): |
51 |
'''The buffer could have grown by a maximum of BUF_SIZE bytes everytime |
|
52 |
this method is called. Retains state across method calls and if a byte |
|
53 |
has been read it will not be parsed again. |
|
54 |
''' |
|
46 | 55 |
delim = MSG_DELIM |
47 | 56 |
n = len(delim) - 1 |
48 | 57 |
state = self._parsing_state |
... | ... | |
61 | 70 |
x = buf.read(1) |
62 | 71 |
if not x: # done reading |
63 | 72 |
break |
64 |
if x==delim[i]: # what we expected
|
|
73 |
if x==delim[state]: # what we expected
|
|
65 | 74 |
state += 1 # expect the next delim char |
66 | 75 |
else: |
67 | 76 |
state = 0 # reset |
... | ... | |
101 | 110 |
self._connected = True |
102 | 111 |
self._post_connect() |
103 | 112 |
|
104 |
|
|
105 | 113 |
def run(self): |
106 | 114 |
chan = self._channel |
107 | 115 |
chan.setblocking(0) |
108 | 116 |
q = self._q |
109 | 117 |
try: |
110 | 118 |
while True: |
111 |
if chan.closed: |
|
112 |
raise SessionCloseError(self._in_buf.getvalue()) |
|
113 |
if chan.send_ready() and not q.empty(): |
|
114 |
data = q.get() + MSG_DELIM |
|
115 |
while data: |
|
116 |
n = chan.send(data) |
|
117 |
if n <= 0: |
|
118 |
raise SessionCloseError(self._in_buf.getvalue(), data) |
|
119 |
data = data[n:] |
|
120 |
if chan.recv_ready(): |
|
119 |
# select on a paramiko ssh channel object does not ever |
|
120 |
# return it in the writable list, so it does not exactly |
|
121 |
# emulate the socket api |
|
122 |
r, w, e = select([chan], [], [], 0.1) |
|
123 |
if r: |
|
121 | 124 |
data = chan.recv(BUF_SIZE) |
122 | 125 |
if data: |
123 | 126 |
self._in_buf.write(data) |
124 | 127 |
self._fresh_data() |
125 | 128 |
else: |
126 | 129 |
raise SessionCloseError(self._in_buf.getvalue()) |
130 |
if not q.empty() and chan.send_ready(): |
|
131 |
data = q.get() + MSG_DELIM |
|
132 |
while data: |
|
133 |
n = chan.send(data) |
|
134 |
if n <= 0: |
|
135 |
raise SessionCloseError(self._in_buf.getvalue(), data) |
|
136 |
data = data[n:] |
|
127 | 137 |
except Exception as e: |
128 | 138 |
logger.debug('*** broke out of main loop ***') |
129 | 139 |
self.dispatch('error', e) |
Also available in: Unified diff