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