Statistics
| Branch: | Tag: | Revision:

root / ncclient / ssh.py @ 927205da

History | View | Annotate | Download (5.1 kB)

1
# Copyright 2009 Shikhar Bhushan
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
6
#
7
#    http://www.apache.org/licenses/LICENSE-2.0
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14

    
15
import logging
16
import paramiko
17

    
18
from os import SEEK_CUR
19
from cStringIO import StringIO
20

    
21
from session import Session, SessionError
22

    
23
logger = logging.getLogger('ncclient.ssh')
24

    
25

    
26
class SessionCloseError(SessionError):
27
    
28
    def __str__(self):
29
        return 'RECEIVED: %s | UNSENT: %s' % (self._in_buf, self._out_buf)
30
    
31
    def __init__(self, in_buf, out_buf):
32
        SessionError.__init__(self)
33
        self._in_buf, self._out_buf = in_buf, out_buf
34

    
35

    
36
class SSHSession(Session):
37

    
38
    BUF_SIZE = 4096
39
    MSG_DELIM = ']]>>]]'
40
    
41
    
42
    def __init__(self, load_known_hosts=True,
43
                 missing_host_key_policy=paramiko.RejectPolicy):
44
        Session.__init__(self)
45
        self._client = paramiko.SSHClient()
46
        if load_known_hosts:
47
            self._client.load_system_host_keys()
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
    
54
    
55
    def load_host_keys(self, filename):
56
        self._client.load_host_keys(filename)
57
    
58
    
59
    def set_missing_host_key_policy(self, policy):
60
        self._client.set_missing_host_key_policy(policy)
61
    
62
    
63
    # paramiko exceptions ok?
64
    # user might be looking for ClientError
65
    def connect(self, hostname, port=830, username=None, password=None,
66
                key_filename=None, timeout=None, allow_agent=True,
67
                look_for_keys=True):
68
        self._client.connect(hostname, port=port, username=username,
69
                            password=password, key_filename=key_filename,
70
                            timeout=timeout, allow_agent=allow_agent,
71
                            look_for_keys=look_for_keys)    
72
        transport = self._client.get_transport()
73
        self._channel = transport.open_session()
74
        self._channel.invoke_subsystem('netconf')
75
        self._channel.set_name('netconf')
76
        self._connect()
77
    
78
    
79
    def run(self):
80
        
81
        chan = self._channel
82
        chan.setblocking(0)
83
        q = self._q
84
        
85
        while True:
86
            if chan.closed:
87
                break
88
            if chan.recv_ready():
89
                data = chan.recv(SSHSession.BUF_SIZE)
90
                if data:
91
                    self._in_buf.write(data)
92
                    self._parse()
93
                else:
94
                    break
95
            if chan.send_ready():
96
                if not q.empty():
97
                    self._out_buf.write(q.get() + SSHSession.MSG_DELIM)
98
                    self._dump()
99
        
100
        logger.debug('** broke out of main loop **')
101
        self.dispatch('close', SessionCloseError(self._in_buf, self._out_buf))
102
    
103
    
104
    def _close(self):
105
        self._channel.close()
106
        Session._close(self)
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()
157

    
158
class MissingHostKeyPolicy(paramiko.MissingHostKeyPolicy):
159
    
160
    def __init__(self, cb):
161
        self._cb = cb
162
    
163
    def missing_host_key(self, client, hostname, key):
164
        if not self._cb(hostname, key):
165
            raise SSHError