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