1 # paramiko_vendor.py -- paramiko implementation of the 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_vendor import ParamikoSSHVendor
26 >>> _mod_client.get_ssh_vendor = ParamikoSSHVendor
28 This implementation is experimental and does not have any tests.
32 import paramiko.client
36 class _ParamikoWrapper(object):
37 STDERR_READ_N = 2048 # 2k
39 def __init__(self, client, channel, progress_stderr=None):
41 self.channel = channel
42 self.progress_stderr = progress_stderr
43 self.should_monitor = bool(progress_stderr) or True
44 self.monitor_thread = None
48 self.channel.setblocking(True)
51 if self.should_monitor:
52 self.monitor_thread = threading.Thread(
53 target=self.monitor_stderr)
54 self.monitor_thread.start()
56 def monitor_stderr(self):
57 while self.should_monitor:
59 data = self.read_stderr(self.STDERR_READ_N)
63 self.should_monitor = False
67 if self.progress_stderr:
68 self.progress_stderr(data)
73 def stop_monitoring(self):
75 if self.should_monitor:
76 self.should_monitor = False
77 self.monitor_thread.join()
80 data = self.channel.in_stderr_buffer.empty()
84 return self.channel.recv_ready()
86 def write(self, data):
87 return self.channel.sendall(data)
89 def read_stderr(self, n):
90 return self.channel.recv_stderr(n)
92 def read(self, n=None):
93 data = self.channel.recv(n)
100 # Read more if needed
101 if n and data_len < n:
102 diff_len = n - data_len
103 return data + self.read(diff_len)
108 self.stop_monitoring()
111 class ParamikoSSHVendor(object):
116 def run_command(self, host, command, username=None, port=None,
117 progress_stderr=None):
118 if (type(command) is not list or
119 not all([isinstance(b, bytes) for b in command])):
120 raise TypeError(command)
121 # Paramiko needs an explicit port. None is not valid
125 client = paramiko.SSHClient()
127 policy = paramiko.client.MissingHostKeyPolicy()
128 client.set_missing_host_key_policy(policy)
129 client.connect(host, username=username, port=port,
133 channel = client.get_transport().open_session()
136 channel.exec_command(subprocess.list2cmdline(command))
138 return _ParamikoWrapper(
139 client, channel, progress_stderr=progress_stderr)