Revision 927205da
b/ncclient/ssh.py | ||
---|---|---|
15 | 15 |
import logging |
16 | 16 |
import paramiko |
17 | 17 |
|
18 |
from os import SEEK_CUR |
|
19 |
from cStringIO import StringIO |
|
20 |
|
|
18 | 21 |
from session import Session, SessionError |
19 | 22 |
|
20 | 23 |
logger = logging.getLogger('ncclient.ssh') |
21 | 24 |
|
25 |
|
|
22 | 26 |
class SessionCloseError(SessionError): |
23 | 27 |
|
24 | 28 |
def __str__(self): |
... | ... | |
28 | 32 |
SessionError.__init__(self) |
29 | 33 |
self._in_buf, self._out_buf = in_buf, out_buf |
30 | 34 |
|
35 |
|
|
31 | 36 |
class SSHSession(Session): |
32 | 37 |
|
33 | 38 |
BUF_SIZE = 4096 |
34 |
MSG_DELIM = ']]>]]>' |
|
39 |
MSG_DELIM = ']]>>]]' |
|
40 |
|
|
35 | 41 |
|
36 | 42 |
def __init__(self, load_known_hosts=True, |
37 | 43 |
missing_host_key_policy=paramiko.RejectPolicy): |
38 | 44 |
Session.__init__(self) |
39 |
self._in_buf = '' |
|
40 |
self._out_buf = '' |
|
41 | 45 |
self._client = paramiko.SSHClient() |
42 | 46 |
if load_known_hosts: |
43 | 47 |
self._client.load_system_host_keys() |
44 | 48 |
self._client.set_missing_host_key_policy(missing_host_key_policy) |
49 |
self._in_buf = StringIO() |
|
50 |
self._out_buf = StringIO() |
|
51 |
self._parsing_state = -1 |
|
52 |
self._parsing_pos = 0 |
|
53 |
|
|
45 | 54 |
|
46 | 55 |
def load_host_keys(self, filename): |
47 | 56 |
self._client.load_host_keys(filename) |
48 | 57 |
|
58 |
|
|
49 | 59 |
def set_missing_host_key_policy(self, policy): |
50 | 60 |
self._client.set_missing_host_key_policy(policy) |
51 | 61 |
|
62 |
|
|
52 | 63 |
# paramiko exceptions ok? |
53 | 64 |
# user might be looking for ClientError |
54 | 65 |
def connect(self, hostname, port=830, username=None, password=None, |
... | ... | |
64 | 75 |
self._channel.set_name('netconf') |
65 | 76 |
self._connect() |
66 | 77 |
|
78 |
|
|
67 | 79 |
def run(self): |
68 | 80 |
|
69 | 81 |
chan = self._channel |
... | ... | |
71 | 83 |
q = self._q |
72 | 84 |
|
73 | 85 |
while True: |
74 |
|
|
75 | 86 |
if chan.closed: |
76 | 87 |
break |
77 |
|
|
78 | 88 |
if chan.recv_ready(): |
79 | 89 |
data = chan.recv(SSHSession.BUF_SIZE) |
80 | 90 |
if data: |
81 |
self._in_buf += data |
|
82 |
while True: |
|
83 |
before, delim, after = self._in_buf.partition(SSHSession.MSG_DELIM) |
|
84 |
if delim: |
|
85 |
self.dispatch('reply', before) |
|
86 |
self._in_buf = after |
|
87 |
else: |
|
88 |
break |
|
91 |
self._in_buf.write(data) |
|
92 |
self._parse() |
|
89 | 93 |
else: |
90 | 94 |
break |
91 |
|
|
92 | 95 |
if chan.send_ready(): |
93 | 96 |
if not q.empty(): |
94 |
msg = q.get() |
|
95 |
self._out_buf += ( msg + SSHSession.MSG_DELIM ) |
|
96 |
while self._out_buf: |
|
97 |
n = chan.send(self._out_buf) |
|
98 |
if n <= 0: |
|
99 |
break |
|
100 |
self._out_buf = self._out_buf[n:] |
|
97 |
self._out_buf.write(q.get() + SSHSession.MSG_DELIM) |
|
98 |
self._dump() |
|
101 | 99 |
|
102 | 100 |
logger.debug('** broke out of main loop **') |
103 | 101 |
self.dispatch('close', SessionCloseError(self._in_buf, self._out_buf)) |
104 | 102 |
|
103 |
|
|
105 | 104 |
def _close(self): |
106 | 105 |
self._channel.close() |
107 | 106 |
Session._close(self) |
108 |
|
|
107 |
|
|
108 |
def _dump(self): |
|
109 |
for line in self._out_buf: |
|
110 |
while line: |
|
111 |
n = chan.send(line) |
|
112 |
if n <= 0: |
|
113 |
break |
|
114 |
line = self._out_buf[n:] |
|
115 |
|
|
116 |
def _parse(self): |
|
117 |
delim = SSHSession.MSG_DELIM |
|
118 |
n = len(delim) - 1 |
|
119 |
state = self._parsing_state |
|
120 |
buf = self._in_buf |
|
121 |
buf.seek(self._parsing_pos) |
|
122 |
|
|
123 |
while True: |
|
124 |
|
|
125 |
x = buf.read(1) |
|
126 |
if not x: # done reading |
|
127 |
break |
|
128 |
elif x == delim[state]: |
|
129 |
state += 1 |
|
130 |
else: |
|
131 |
continue |
|
132 |
# loop till last delim char expected, break if other char encountered |
|
133 |
for i in range(state, n): |
|
134 |
x = buf.read(1) |
|
135 |
if not x: # done reading |
|
136 |
break |
|
137 |
if x==delim[i]: # what we expected |
|
138 |
state += 1 # expect the next delim char |
|
139 |
else: |
|
140 |
state = 0 # reset |
|
141 |
break |
|
142 |
else: # if we didn't break out of above loop, full delim parsed |
|
143 |
till = buf.tell() - n |
|
144 |
buf.seek(0) |
|
145 |
msg = buf.read(till) |
|
146 |
self.dispatch('reply', msg) |
|
147 |
buf.seek(n+1, SEEK_CUR) |
|
148 |
rest = buf.read() |
|
149 |
buf = StringIO() |
|
150 |
buf.write(rest) |
|
151 |
buf.seek(0) |
|
152 |
state = 0 |
|
153 |
|
|
154 |
self._parsing_state = state |
|
155 |
self._in_buf = buf |
|
156 |
self._parsing_pos = self._in_buf.tell() |
|
109 | 157 |
|
110 | 158 |
class MissingHostKeyPolicy(paramiko.MissingHostKeyPolicy): |
111 | 159 |
|
Also available in: Unified diff