1 # Copyright 2009 Shikhar Bhushan
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 from cStringIO import StringIO
17 from os import SEEK_CUR
23 from session import Session, SessionError
25 logger = logging.getLogger('ncclient.ssh')
28 class SessionCloseError(SessionError):
31 return 'RECEIVED: %s | UNSENT: %s' % (self._in_buf, self._out_buf)
33 def __init__(self, in_buf, out_buf=None):
34 SessionError.__init__(self)
35 self._in_buf, self._out_buf = in_buf, out_buf
38 class SSHSession(Session):
43 def __init__(self, load_known_hosts=True,
44 missing_host_key_policy=paramiko.RejectPolicy()):
45 Session.__init__(self)
46 self._client = paramiko.SSHClient()
49 self._client.load_system_host_keys()
50 self._client.set_missing_host_key_policy(missing_host_key_policy)
51 self._in_buf = StringIO()
52 self._parsing_state = 0
57 self._connected = False
59 def _fresh_data(self):
60 delim = SSHSession.MSG_DELIM
62 state = self._parsing_state
64 buf.seek(self._parsing_pos)
67 if not x: # done reading
69 elif x == delim[state]:
73 # loop till last delim char expected, break if other char encountered
74 for i in range(state, n):
76 if not x: # done reading
78 if x==delim[i]: # what we expected
79 state += 1 # expect the next delim char
83 else: # if we didn't break out of above loop, full delim parsed
87 self.dispatch('reply', msg)
88 buf.seek(n+1, SEEK_CUR)
95 self._parsing_state = state
96 self._parsing_pos = self._in_buf.tell()
98 #def load_host_keys(self, filename):
99 # self._client.load_host_keys(filename)
101 #def set_missing_host_key_policy(self, policy):
102 # self._client.set_missing_host_key_policy(policy)
104 #def connect(self, hostname, port=830, username=None, password=None,
105 # key_filename=None, timeout=None, allow_agent=True,
106 # look_for_keys=True):
107 # self._client.connect(hostname, port=port, username=username,
108 # password=password, key_filename=key_filename,
109 # timeout=timeout, allow_agent=allow_agent,
110 # look_for_keys=look_for_keys)
111 # transport = self._client.get_transport()
112 # self._channel = transport.open_session()
113 # self._channel.invoke_subsystem('netconf')
114 # self._channel.set_name('netconf')
115 # self._connected = True
116 # self._post_connect()
118 def connect(self, hostname, port=830, username=None, password=None,
119 key_filename=None, timeout=None, allow_agent=True,
121 self._transport = paramiko.Transport()
130 raise SessionCloseError(self._in_buf.getvalue())
131 if chan.send_ready() and not q.empty():
132 data = q.get() + SSHSession.MSG_DELIM
136 raise SessionCloseError(self._in_buf.getvalue(), data)
138 if chan.recv_ready():
139 data = chan.recv(SSHSession.BUF_SIZE)
141 self._in_buf.write(data)
144 raise SessionCloseError(self._in_buf.getvalue())
145 except Exception as e:
146 logger.debug('*** broke out of main loop ***')
147 self.dispatch('error', e)
149 class MissingHostKeyPolicy(paramiko.MissingHostKeyPolicy):
151 def __init__(self, cb):
154 def missing_host_key(self, client, hostname, key):
155 if not self._cb(hostname, key):