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