1 # paramako.py -- paramiko implementation of the Dulwich SSHVendor interface
2 # Copyright (C) 2013 Aaron O'Mullan <aaron.omullan@friendco.de>
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # or (at your option) a later version of the License.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 """Paramiko SSH support for Dulwich.
21 To use this implementation as the SSH implementation in Dulwich, override
22 the dulwich.client.get_ssh_vendor attribute:
24 >>> from dulwich import client as _mod_client
25 >>> from dulwich.contrib.paramiko import ParamikoSSHVendor
26 >>> _mod_client.get_ssh_vendor = ParamikoSSHVendor
28 This implementation is experimental and does not have any tests.
35 class _ParamikoWrapper(object):
36 STDERR_READ_N = 2048 # 2k
38 def __init__(self, client, channel, progress_stderr=None):
40 self.channel = channel
41 self.progress_stderr = progress_stderr
42 self.should_monitor = bool(progress_stderr) or True
43 self.monitor_thread = None
47 self.channel.setblocking(True)
50 if self.should_monitor:
51 self.monitor_thread = threading.Thread(
52 target=self.monitor_stderr)
53 self.monitor_thread.start()
55 def monitor_stderr(self):
56 while self.should_monitor:
58 data = self.read_stderr(self.STDERR_READ_N)
62 self.should_monitor = False
66 if self.progress_stderr:
67 self.progress_stderr(data)
72 def stop_monitoring(self):
74 if self.should_monitor:
75 self.should_monitor = False
76 self.monitor_thread.join()
79 data = self.channel.in_stderr_buffer.empty()
83 return self.channel.recv_ready()
85 def write(self, data):
86 return self.channel.sendall(data)
88 def read_stderr(self, n):
89 return self.channel.recv_stderr(n)
91 def read(self, n=None):
92 data = self.channel.recv(n)
100 if n and data_len < n:
101 diff_len = n - data_len
102 return data + self.read(diff_len)
107 self.stop_monitoring()
110 class ParamikoSSHVendor(object):
115 def run_command(self, host, command, username=None, port=None,
116 progress_stderr=None):
117 if (type(command) is not list or
118 not all([isinstance(b, bytes) for b in command])):
119 raise TypeError(command)
120 # Paramiko needs an explicit port. None is not valid
124 client = paramiko.SSHClient()
126 policy = paramiko.client.MissingHostKeyPolicy()
127 client.set_missing_host_key_policy(policy)
128 client.connect(host, username=username, port=port,
132 channel = client.get_transport().open_session()
135 channel.exec_command(subprocess.list2cmdline(command))
137 return _ParamikoWrapper(
138 client, channel, progress_stderr=progress_stderr)