root / ncclient / transport / ssh.py @ 4a3d4804
History | View | Annotate | Download (12.1 kB)
1 | d095a59e | Shikhar Bhushan | # Copyright 2009 Shikhar Bhushan
|
---|---|---|---|
2 | d095a59e | Shikhar Bhushan | #
|
3 | d095a59e | Shikhar Bhushan | # Licensed under the Apache License, Version 2.0 (the "License");
|
4 | d095a59e | Shikhar Bhushan | # you may not use this file except in compliance with the License.
|
5 | d095a59e | Shikhar Bhushan | # You may obtain a copy of the License at
|
6 | d095a59e | Shikhar Bhushan | #
|
7 | d095a59e | Shikhar Bhushan | # http://www.apache.org/licenses/LICENSE-2.0
|
8 | d095a59e | Shikhar Bhushan | #
|
9 | d095a59e | Shikhar Bhushan | # Unless required by applicable law or agreed to in writing, software
|
10 | d095a59e | Shikhar Bhushan | # distributed under the License is distributed on an "AS IS" BASIS,
|
11 | d095a59e | Shikhar Bhushan | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 | d095a59e | Shikhar Bhushan | # See the License for the specific language governing permissions and
|
13 | d095a59e | Shikhar Bhushan | # limitations under the License.
|
14 | d095a59e | Shikhar Bhushan | |
15 | d095a59e | Shikhar Bhushan | import os |
16 | d095a59e | Shikhar Bhushan | import socket |
17 | c34a55c8 | Shikhar Bhushan | import getpass |
18 | d095a59e | Shikhar Bhushan | from binascii import hexlify |
19 | d095a59e | Shikhar Bhushan | from cStringIO import StringIO |
20 | d095a59e | Shikhar Bhushan | from select import select |
21 | d095a59e | Shikhar Bhushan | |
22 | d095a59e | Shikhar Bhushan | import paramiko |
23 | d095a59e | Shikhar Bhushan | |
24 | c35cebbf | Shikhar Bhushan | from errors import AuthenticationError, SessionCloseError, SSHError, SSHUnknownHostError |
25 | d095a59e | Shikhar Bhushan | from session import Session |
26 | d095a59e | Shikhar Bhushan | |
27 | 41e2ed46 | Shikhar Bhushan | import logging |
28 | 4bc8021f | Shikhar Bhushan | logger = logging.getLogger("ncclient.transport.ssh")
|
29 | 41e2ed46 | Shikhar Bhushan | |
30 | d095a59e | Shikhar Bhushan | BUF_SIZE = 4096
|
31 | 030b950d | Shikhar Bhushan | MSG_DELIM = "]]>]]>"
|
32 | d095a59e | Shikhar Bhushan | TICK = 0.1
|
33 | d095a59e | Shikhar Bhushan | |
34 | 9a9af391 | Shikhar Bhushan | def default_unknown_host_cb(host, fingerprint): |
35 | 19e7c7f6 | Shikhar Bhushan | """An unknown host callback returns `True` if it finds the key acceptable, and `False` if not.
|
36 | 4f650d54 | Shikhar Bhushan |
|
37 | 19e7c7f6 | Shikhar Bhushan | This default callback always returns `False`, which would lead to :meth:`connect` raising a :exc:`SSHUnknownHost` exception.
|
38 | 4bc8021f | Shikhar Bhushan |
|
39 | 4bc8021f | Shikhar Bhushan | Supply another valid callback if you need to verify the host key programatically.
|
40 | 216bb34c | Shikhar Bhushan |
|
41 | 19e7c7f6 | Shikhar Bhushan | *host* is the hostname that needs to be verified
|
42 | 4f650d54 | Shikhar Bhushan |
|
43 | 19e7c7f6 | Shikhar Bhushan | *fingerprint* is a hex string representing the host key fingerprint, colon-delimited e.g. `"4b:69:6c:72:6f:79:20:77:61:73:20:68:65:72:65:21"`
|
44 | 4f650d54 | Shikhar Bhushan | """
|
45 | 4f650d54 | Shikhar Bhushan | return False |
46 | 4f650d54 | Shikhar Bhushan | |
47 | 9a9af391 | Shikhar Bhushan | def _colonify(fp): |
48 | 9a9af391 | Shikhar Bhushan | finga = fp[:2]
|
49 | 9a9af391 | Shikhar Bhushan | for idx in range(2, len(fp), 2): |
50 | 9a9af391 | Shikhar Bhushan | finga += ":" + fp[idx:idx+2] |
51 | 9a9af391 | Shikhar Bhushan | return finga
|
52 | 4f650d54 | Shikhar Bhushan | |
53 | d095a59e | Shikhar Bhushan | class SSHSession(Session): |
54 | 4f650d54 | Shikhar Bhushan | |
55 | 4f650d54 | Shikhar Bhushan | "Implements a :rfc:`4742` NETCONF session over SSH."
|
56 | 4f650d54 | Shikhar Bhushan | |
57 | 583c11f6 | Shikhar Bhushan | def __init__(self, capabilities): |
58 | 583c11f6 | Shikhar Bhushan | Session.__init__(self, capabilities)
|
59 | d095a59e | Shikhar Bhushan | self._host_keys = paramiko.HostKeys()
|
60 | d095a59e | Shikhar Bhushan | self._transport = None |
61 | d095a59e | Shikhar Bhushan | self._connected = False |
62 | d095a59e | Shikhar Bhushan | self._channel = None |
63 | d095a59e | Shikhar Bhushan | self._buffer = StringIO() # for incoming data |
64 | d095a59e | Shikhar Bhushan | # parsing-related, see _parse()
|
65 | 4f650d54 | Shikhar Bhushan | self._parsing_state = 0 |
66 | d095a59e | Shikhar Bhushan | self._parsing_pos = 0 |
67 | 9a9af391 | Shikhar Bhushan | |
68 | d095a59e | Shikhar Bhushan | def _parse(self): |
69 | 19e7c7f6 | Shikhar Bhushan | "Messages ae delimited by MSG_DELIM. The buffer could have grown by a maximum of BUF_SIZE bytes everytime this method is called. Retains state across method calls and if a byte has been read it will not be considered again."
|
70 | d095a59e | Shikhar Bhushan | delim = MSG_DELIM |
71 | d095a59e | Shikhar Bhushan | n = len(delim) - 1 |
72 | d095a59e | Shikhar Bhushan | expect = self._parsing_state
|
73 | d095a59e | Shikhar Bhushan | buf = self._buffer
|
74 | d095a59e | Shikhar Bhushan | buf.seek(self._parsing_pos)
|
75 | d095a59e | Shikhar Bhushan | while True: |
76 | d095a59e | Shikhar Bhushan | x = buf.read(1)
|
77 | d095a59e | Shikhar Bhushan | if not x: # done reading |
78 | d095a59e | Shikhar Bhushan | break
|
79 | d095a59e | Shikhar Bhushan | elif x == delim[expect]: # what we expected |
80 | d095a59e | Shikhar Bhushan | expect += 1 # expect the next delim char |
81 | d095a59e | Shikhar Bhushan | else:
|
82 | 0c608b53 | Shikhar Bhushan | expect = 0
|
83 | d095a59e | Shikhar Bhushan | continue
|
84 | d095a59e | Shikhar Bhushan | # loop till last delim char expected, break if other char encountered
|
85 | d095a59e | Shikhar Bhushan | for i in range(expect, n): |
86 | d095a59e | Shikhar Bhushan | x = buf.read(1)
|
87 | d095a59e | Shikhar Bhushan | if not x: # done reading |
88 | d095a59e | Shikhar Bhushan | break
|
89 | d095a59e | Shikhar Bhushan | if x == delim[expect]: # what we expected |
90 | d095a59e | Shikhar Bhushan | expect += 1 # expect the next delim char |
91 | d095a59e | Shikhar Bhushan | else:
|
92 | d095a59e | Shikhar Bhushan | expect = 0 # reset |
93 | d095a59e | Shikhar Bhushan | break
|
94 | d095a59e | Shikhar Bhushan | else: # if we didn't break out of the loop, full delim was parsed |
95 | d095a59e | Shikhar Bhushan | msg_till = buf.tell() - n |
96 | d095a59e | Shikhar Bhushan | buf.seek(0)
|
97 | 41e2ed46 | Shikhar Bhushan | logger.debug('parsed new message')
|
98 | 41e2ed46 | Shikhar Bhushan | self._dispatch_message(buf.read(msg_till).strip())
|
99 | d095a59e | Shikhar Bhushan | buf.seek(n+1, os.SEEK_CUR)
|
100 | d095a59e | Shikhar Bhushan | rest = buf.read() |
101 | d095a59e | Shikhar Bhushan | buf = StringIO() |
102 | d095a59e | Shikhar Bhushan | buf.write(rest) |
103 | d095a59e | Shikhar Bhushan | buf.seek(0)
|
104 | 4ba5e843 | Shikhar Bhushan | expect = 0
|
105 | d095a59e | Shikhar Bhushan | self._buffer = buf
|
106 | d095a59e | Shikhar Bhushan | self._parsing_state = expect
|
107 | d095a59e | Shikhar Bhushan | self._parsing_pos = self._buffer.tell() |
108 | 4f650d54 | Shikhar Bhushan | |
109 | 216bb34c | Shikhar Bhushan | def load_known_hosts(self, filename=None): |
110 | 19e7c7f6 | Shikhar Bhushan | """Load host keys from an openssh :file:`known_hosts`-style file. Can be called multiple times.
|
111 | 216bb34c | Shikhar Bhushan |
|
112 | 19e7c7f6 | Shikhar Bhushan | If *filename* is not specified, looks in the default locations i.e. :file:`~/.ssh/known_hosts` and :file:`~/ssh/known_hosts` for Windows.
|
113 | 216bb34c | Shikhar Bhushan | """
|
114 | d095a59e | Shikhar Bhushan | if filename is None: |
115 | d095a59e | Shikhar Bhushan | filename = os.path.expanduser('~/.ssh/known_hosts')
|
116 | d095a59e | Shikhar Bhushan | try:
|
117 | 216bb34c | Shikhar Bhushan | self._host_keys.load(filename)
|
118 | d095a59e | Shikhar Bhushan | except IOError: |
119 | 4ba5e843 | Shikhar Bhushan | # for windows
|
120 | 4ba5e843 | Shikhar Bhushan | filename = os.path.expanduser('~/ssh/known_hosts')
|
121 | 4ba5e843 | Shikhar Bhushan | try:
|
122 | 216bb34c | Shikhar Bhushan | self._host_keys.load(filename)
|
123 | 4ba5e843 | Shikhar Bhushan | except IOError: |
124 | 4ba5e843 | Shikhar Bhushan | pass
|
125 | 216bb34c | Shikhar Bhushan | else:
|
126 | 216bb34c | Shikhar Bhushan | self._host_keys.load(filename)
|
127 | 4f650d54 | Shikhar Bhushan | |
128 | d095a59e | Shikhar Bhushan | def close(self): |
129 | d095a59e | Shikhar Bhushan | if self._transport.is_active(): |
130 | d095a59e | Shikhar Bhushan | self._transport.close()
|
131 | d095a59e | Shikhar Bhushan | self._connected = False |
132 | 4f650d54 | Shikhar Bhushan | |
133 | 19e7c7f6 | Shikhar Bhushan | # REMEMBER to update transport.rst if sig. changes, since it is hardcoded there
|
134 | 19e7c7f6 | Shikhar Bhushan | def connect(self, host, port=830, timeout=None, unknown_host_cb=default_unknown_host_cb, |
135 | 19e7c7f6 | Shikhar Bhushan | username=None, password=None, key_filename=None, allow_agent=True, look_for_keys=True): |
136 | 19e7c7f6 | Shikhar Bhushan | """Connect via SSH and initialize the NETCONF session. First attempts the publickey authentication method and then password authentication.
|
137 | 4f650d54 | Shikhar Bhushan |
|
138 | 19e7c7f6 | Shikhar Bhushan | To disable attempting publickey authentication altogether, call with *allow_agent* and *look_for_keys* as `False`.
|
139 | 4f650d54 | Shikhar Bhushan |
|
140 | d215b592 | Shikhar Bhushan | *host* is the hostname or IP address to connect to
|
141 | 4f650d54 | Shikhar Bhushan |
|
142 | d215b592 | Shikhar Bhushan | *port* is by default 830, but some devices use the default SSH port of 22 so this may need to be specified
|
143 | 4f650d54 | Shikhar Bhushan |
|
144 | d215b592 | Shikhar Bhushan | *timeout* is an optional timeout for socket connect
|
145 | 4f650d54 | Shikhar Bhushan |
|
146 | d215b592 | Shikhar Bhushan | *unknown_host_cb* is called when the server host key is not recognized. It takes two arguments, the hostname and the fingerprint (see the signature of :func:`default_unknown_host_cb`)
|
147 | 4f650d54 | Shikhar Bhushan |
|
148 | d215b592 | Shikhar Bhushan | *username* is the username to use for SSH authentication
|
149 | 4f650d54 | Shikhar Bhushan |
|
150 | d215b592 | Shikhar Bhushan | *password* is the password used if using password authentication, or the passphrase to use for unlocking keys that require it
|
151 | 4f650d54 | Shikhar Bhushan |
|
152 | d215b592 | Shikhar Bhushan | *key_filename* is a filename where a the private key to be used can be found
|
153 | 4f650d54 | Shikhar Bhushan |
|
154 | d215b592 | Shikhar Bhushan | *allow_agent* enables querying SSH agent (if found) for keys
|
155 | 4f650d54 | Shikhar Bhushan |
|
156 | d215b592 | Shikhar Bhushan | *look_for_keys* enables looking in the usual locations for ssh keys (e.g. :file:`~/.ssh/id_*`)
|
157 | 4f650d54 | Shikhar Bhushan | """
|
158 | bb700ea5 | Shikhar Bhushan | if username is None: |
159 | 68ac4439 | Shikhar Bhushan | username = getpass.getuser() |
160 | 68ac4439 | Shikhar Bhushan | |
161 | bb700ea5 | Shikhar Bhushan | sock = None
|
162 | bb700ea5 | Shikhar Bhushan | for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): |
163 | bb700ea5 | Shikhar Bhushan | af, socktype, proto, canonname, sa = res |
164 | bb700ea5 | Shikhar Bhushan | try:
|
165 | bb700ea5 | Shikhar Bhushan | sock = socket.socket(af, socktype, proto) |
166 | bb700ea5 | Shikhar Bhushan | sock.settimeout(timeout) |
167 | bb700ea5 | Shikhar Bhushan | except socket.error:
|
168 | bb700ea5 | Shikhar Bhushan | continue
|
169 | bb700ea5 | Shikhar Bhushan | try:
|
170 | bb700ea5 | Shikhar Bhushan | sock.connect(sa) |
171 | bb700ea5 | Shikhar Bhushan | except socket.error:
|
172 | bb700ea5 | Shikhar Bhushan | sock.close() |
173 | bb700ea5 | Shikhar Bhushan | continue
|
174 | bb700ea5 | Shikhar Bhushan | break
|
175 | d095a59e | Shikhar Bhushan | else:
|
176 | 495b9bf7 | Shikhar Bhushan | raise SSHError("Could not open socket to %s:%s" % (host, port)) |
177 | bb700ea5 | Shikhar Bhushan | |
178 | d095a59e | Shikhar Bhushan | t = self._transport = paramiko.Transport(sock)
|
179 | d095a59e | Shikhar Bhushan | t.set_log_channel(logger.name) |
180 | 4f650d54 | Shikhar Bhushan | |
181 | d095a59e | Shikhar Bhushan | try:
|
182 | d095a59e | Shikhar Bhushan | t.start_client() |
183 | d095a59e | Shikhar Bhushan | except paramiko.SSHException:
|
184 | d095a59e | Shikhar Bhushan | raise SSHError('Negotiation failed') |
185 | 4f650d54 | Shikhar Bhushan | |
186 | d095a59e | Shikhar Bhushan | # host key verification
|
187 | d095a59e | Shikhar Bhushan | server_key = t.get_remote_server_key() |
188 | 216bb34c | Shikhar Bhushan | known_host = self._host_keys.check(host, server_key)
|
189 | 216bb34c | Shikhar Bhushan | |
190 | 9a9af391 | Shikhar Bhushan | fingerprint = _colonify(hexlify(server_key.get_fingerprint())) |
191 | 4f650d54 | Shikhar Bhushan | |
192 | 216bb34c | Shikhar Bhushan | if not known_host and not unknown_host_cb(host, fingerprint): |
193 | 216bb34c | Shikhar Bhushan | raise SSHUnknownHostError(host, fingerprint)
|
194 | 4f650d54 | Shikhar Bhushan | |
195 | d095a59e | Shikhar Bhushan | if key_filename is None: |
196 | d095a59e | Shikhar Bhushan | key_filenames = [] |
197 | d095a59e | Shikhar Bhushan | elif isinstance(key_filename, basestring): |
198 | d095a59e | Shikhar Bhushan | key_filenames = [ key_filename ] |
199 | d095a59e | Shikhar Bhushan | else:
|
200 | d095a59e | Shikhar Bhushan | key_filenames = key_filename |
201 | 4f650d54 | Shikhar Bhushan | |
202 | d095a59e | Shikhar Bhushan | self._auth(username, password, key_filenames, allow_agent, look_for_keys)
|
203 | 4f650d54 | Shikhar Bhushan | |
204 | d095a59e | Shikhar Bhushan | self._connected = True # there was no error authenticating |
205 | 4f650d54 | Shikhar Bhushan | |
206 | d095a59e | Shikhar Bhushan | c = self._channel = self._transport.open_session() |
207 | 030b950d | Shikhar Bhushan | c.set_name("netconf")
|
208 | 030b950d | Shikhar Bhushan | c.invoke_subsystem("netconf")
|
209 | 4f650d54 | Shikhar Bhushan | |
210 | d095a59e | Shikhar Bhushan | self._post_connect()
|
211 | 9a9af391 | Shikhar Bhushan | |
212 | d095a59e | Shikhar Bhushan | # on the lines of paramiko.SSHClient._auth()
|
213 | d095a59e | Shikhar Bhushan | def _auth(self, username, password, key_filenames, allow_agent, |
214 | d095a59e | Shikhar Bhushan | look_for_keys): |
215 | d095a59e | Shikhar Bhushan | saved_exception = None
|
216 | 4f650d54 | Shikhar Bhushan | |
217 | d095a59e | Shikhar Bhushan | for key_filename in key_filenames: |
218 | d095a59e | Shikhar Bhushan | for cls in (paramiko.RSAKey, paramiko.DSSKey): |
219 | d095a59e | Shikhar Bhushan | try:
|
220 | d095a59e | Shikhar Bhushan | key = cls.from_private_key_file(key_filename, password) |
221 | 030b950d | Shikhar Bhushan | logger.debug("Trying key %s from %s" %
|
222 | d095a59e | Shikhar Bhushan | (hexlify(key.get_fingerprint()), key_filename)) |
223 | d095a59e | Shikhar Bhushan | self._transport.auth_publickey(username, key)
|
224 | d095a59e | Shikhar Bhushan | return
|
225 | d095a59e | Shikhar Bhushan | except Exception as e: |
226 | d095a59e | Shikhar Bhushan | saved_exception = e |
227 | d095a59e | Shikhar Bhushan | logger.debug(e) |
228 | 4f650d54 | Shikhar Bhushan | |
229 | d095a59e | Shikhar Bhushan | if allow_agent:
|
230 | d095a59e | Shikhar Bhushan | for key in paramiko.Agent().get_keys(): |
231 | d095a59e | Shikhar Bhushan | try:
|
232 | 030b950d | Shikhar Bhushan | logger.debug("Trying SSH agent key %s" %
|
233 | d095a59e | Shikhar Bhushan | hexlify(key.get_fingerprint())) |
234 | d095a59e | Shikhar Bhushan | self._transport.auth_publickey(username, key)
|
235 | d095a59e | Shikhar Bhushan | return
|
236 | d095a59e | Shikhar Bhushan | except Exception as e: |
237 | d095a59e | Shikhar Bhushan | saved_exception = e |
238 | d095a59e | Shikhar Bhushan | logger.debug(e) |
239 | 4f650d54 | Shikhar Bhushan | |
240 | d095a59e | Shikhar Bhushan | keyfiles = [] |
241 | d095a59e | Shikhar Bhushan | if look_for_keys:
|
242 | 030b950d | Shikhar Bhushan | rsa_key = os.path.expanduser("~/.ssh/id_rsa")
|
243 | 030b950d | Shikhar Bhushan | dsa_key = os.path.expanduser("~/.ssh/id_dsa")
|
244 | d095a59e | Shikhar Bhushan | if os.path.isfile(rsa_key):
|
245 | d095a59e | Shikhar Bhushan | keyfiles.append((paramiko.RSAKey, rsa_key)) |
246 | d095a59e | Shikhar Bhushan | if os.path.isfile(dsa_key):
|
247 | d095a59e | Shikhar Bhushan | keyfiles.append((paramiko.DSSKey, dsa_key)) |
248 | d095a59e | Shikhar Bhushan | # look in ~/ssh/ for windows users:
|
249 | 030b950d | Shikhar Bhushan | rsa_key = os.path.expanduser("~/ssh/id_rsa")
|
250 | 030b950d | Shikhar Bhushan | dsa_key = os.path.expanduser("~/ssh/id_dsa")
|
251 | d095a59e | Shikhar Bhushan | if os.path.isfile(rsa_key):
|
252 | d095a59e | Shikhar Bhushan | keyfiles.append((paramiko.RSAKey, rsa_key)) |
253 | d095a59e | Shikhar Bhushan | if os.path.isfile(dsa_key):
|
254 | d095a59e | Shikhar Bhushan | keyfiles.append((paramiko.DSSKey, dsa_key)) |
255 | 4f650d54 | Shikhar Bhushan | |
256 | d095a59e | Shikhar Bhushan | for cls, filename in keyfiles: |
257 | d095a59e | Shikhar Bhushan | try:
|
258 | d095a59e | Shikhar Bhushan | key = cls.from_private_key_file(filename, password) |
259 | 030b950d | Shikhar Bhushan | logger.debug("Trying discovered key %s in %s" %
|
260 | d095a59e | Shikhar Bhushan | (hexlify(key.get_fingerprint()), filename)) |
261 | d095a59e | Shikhar Bhushan | self._transport.auth_publickey(username, key)
|
262 | d095a59e | Shikhar Bhushan | return
|
263 | d095a59e | Shikhar Bhushan | except Exception as e: |
264 | d095a59e | Shikhar Bhushan | saved_exception = e |
265 | d095a59e | Shikhar Bhushan | logger.debug(e) |
266 | 4f650d54 | Shikhar Bhushan | |
267 | d095a59e | Shikhar Bhushan | if password is not None: |
268 | d095a59e | Shikhar Bhushan | try:
|
269 | d095a59e | Shikhar Bhushan | self._transport.auth_password(username, password)
|
270 | d095a59e | Shikhar Bhushan | return
|
271 | d095a59e | Shikhar Bhushan | except Exception as e: |
272 | d095a59e | Shikhar Bhushan | saved_exception = e |
273 | d095a59e | Shikhar Bhushan | logger.debug(e) |
274 | 4f650d54 | Shikhar Bhushan | |
275 | d095a59e | Shikhar Bhushan | if saved_exception is not None: |
276 | 541247ba | Shikhar Bhushan | # need pep-3134 to do this right
|
277 | a7cb58ce | Shikhar Bhushan | raise AuthenticationError(repr(saved_exception)) |
278 | 4f650d54 | Shikhar Bhushan | |
279 | 030b950d | Shikhar Bhushan | raise AuthenticationError("No authentication methods available") |
280 | 4f650d54 | Shikhar Bhushan | |
281 | d095a59e | Shikhar Bhushan | def run(self): |
282 | d095a59e | Shikhar Bhushan | chan = self._channel
|
283 | d095a59e | Shikhar Bhushan | q = self._q
|
284 | d095a59e | Shikhar Bhushan | try:
|
285 | d095a59e | Shikhar Bhushan | while True: |
286 | 19e7c7f6 | Shikhar Bhushan | # select on a paramiko ssh channel object does not ever return it in the writable list, so channels don't exactly emulate the socket api
|
287 | d095a59e | Shikhar Bhushan | r, w, e = select([chan], [], [], TICK) |
288 | 19e7c7f6 | Shikhar Bhushan | # will wakeup evey TICK seconds to check if something to send, more if something to read (due to select returning chan in readable list)
|
289 | d095a59e | Shikhar Bhushan | if r:
|
290 | d095a59e | Shikhar Bhushan | data = chan.recv(BUF_SIZE) |
291 | d095a59e | Shikhar Bhushan | if data:
|
292 | d095a59e | Shikhar Bhushan | self._buffer.write(data)
|
293 | d095a59e | Shikhar Bhushan | self._parse()
|
294 | d095a59e | Shikhar Bhushan | else:
|
295 | 94265508 | Shikhar Bhushan | raise SessionCloseError(self._buffer.getvalue()) |
296 | d095a59e | Shikhar Bhushan | if not q.empty() and chan.send_ready(): |
297 | 030b950d | Shikhar Bhushan | logger.debug("Sending message")
|
298 | d095a59e | Shikhar Bhushan | data = q.get() + MSG_DELIM |
299 | d095a59e | Shikhar Bhushan | while data:
|
300 | d095a59e | Shikhar Bhushan | n = chan.send(data) |
301 | d095a59e | Shikhar Bhushan | if n <= 0: |
302 | 94265508 | Shikhar Bhushan | raise SessionCloseError(self._buffer.getvalue(), data) |
303 | d095a59e | Shikhar Bhushan | data = data[n:] |
304 | d095a59e | Shikhar Bhushan | except Exception as e: |
305 | 030b950d | Shikhar Bhushan | logger.debug("Broke out of main loop, error=%r", e)
|
306 | 1d540e60 | Shikhar Bhushan | self.close()
|
307 | 564bee4f | Shikhar Bhushan | self._dispatch_error(e)
|
308 | 4f650d54 | Shikhar Bhushan | |
309 | d095a59e | Shikhar Bhushan | @property
|
310 | d095a59e | Shikhar Bhushan | def transport(self): |
311 | c15671aa | Shikhar Bhushan | "Underlying `paramiko.Transport <http://www.lag.net/paramiko/docs/paramiko.Transport-class.html>`_ object. This makes it possible to call methods like :meth:`~paramiko.Transport.set_keepalive` on it."
|
312 | d095a59e | Shikhar Bhushan | return self._transport |