paramiko_vendor.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. # paramiko_vendor.py -- paramiko implementation of the SSHVendor interface
  2. # Copyright (C) 2013 Aaron O'Mullan <aaron.omullan@friendco.de>
  3. #
  4. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  5. # General Public License as public by the Free Software Foundation; version 2.0
  6. # or (at your option) any later version. You can redistribute it and/or
  7. # modify it under the terms of either of these two licenses.
  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. # You should have received a copy of the licenses; if not, see
  16. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  17. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  18. # License, Version 2.0.
  19. #
  20. """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:
  23. >>> from dulwich import client as _mod_client
  24. >>> from dulwich.contrib.paramiko_vendor import ParamikoSSHVendor
  25. >>> _mod_client.get_ssh_vendor = ParamikoSSHVendor
  26. This implementation is experimental and does not have any tests.
  27. """
  28. import paramiko
  29. import paramiko.client
  30. import threading
  31. class _ParamikoWrapper(object):
  32. STDERR_READ_N = 2048 # 2k
  33. def __init__(self, client, channel):
  34. self.client = client
  35. self.channel = channel
  36. # Channel must block
  37. self.channel.setblocking(True)
  38. @property
  39. def stderr(self):
  40. return self.channel.makefile_stderr()
  41. def can_read(self):
  42. return self.channel.recv_ready()
  43. def write(self, data):
  44. return self.channel.sendall(data)
  45. def read(self, n=None):
  46. data = self.channel.recv(n)
  47. data_len = len(data)
  48. # Closed socket
  49. if not data:
  50. return b''
  51. # Read more if needed
  52. if n and data_len < n:
  53. diff_len = n - data_len
  54. return data + self.read(diff_len)
  55. return data
  56. def close(self):
  57. self.channel.close()
  58. class ParamikoSSHVendor(object):
  59. # http://docs.paramiko.org/en/2.4/api/client.html
  60. def __init__(self, **kwargs):
  61. self.kwargs = kwargs
  62. def run_command(self, host, command,
  63. username=None, port=None,
  64. password=None, pkey=None,
  65. key_filename=None, **kwargs):
  66. client = paramiko.SSHClient()
  67. connection_kwargs = {'hostname': host}
  68. connection_kwargs.update(self.kwargs)
  69. if username:
  70. connection_kwargs['username'] = username
  71. if port:
  72. connection_kwargs['port'] = port
  73. if password:
  74. connection_kwargs['password'] = password
  75. if pkey:
  76. connection_kwargs['pkey'] = pkey
  77. if key_filename:
  78. connection_kwargs['key_filename'] = key_filename
  79. connection_kwargs.update(kwargs)
  80. policy = paramiko.client.MissingHostKeyPolicy()
  81. client.set_missing_host_key_policy(policy)
  82. client.connect(**connection_kwargs)
  83. # Open SSH session
  84. channel = client.get_transport().open_session()
  85. # Run commands
  86. channel.exec_command(command)
  87. return _ParamikoWrapper(client, channel)