paramiko_vendor.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. class _ParamikoWrapper:
  31. def __init__(self, client, channel) -> None:
  32. self.client = client
  33. self.channel = channel
  34. # Channel must block
  35. self.channel.setblocking(True)
  36. @property
  37. def stderr(self):
  38. return self.channel.makefile_stderr("rb")
  39. def can_read(self):
  40. return self.channel.recv_ready()
  41. def write(self, data):
  42. return self.channel.sendall(data)
  43. def read(self, n=None):
  44. data = self.channel.recv(n)
  45. data_len = len(data)
  46. # Closed socket
  47. if not data:
  48. return b""
  49. # Read more if needed
  50. if n and data_len < n:
  51. diff_len = n - data_len
  52. return data + self.read(diff_len)
  53. return data
  54. def close(self):
  55. self.channel.close()
  56. class ParamikoSSHVendor:
  57. # http://docs.paramiko.org/en/2.4/api/client.html
  58. def __init__(self, **kwargs) -> None:
  59. self.kwargs = kwargs
  60. def run_command(
  61. self,
  62. host,
  63. command,
  64. username=None,
  65. port=None,
  66. password=None,
  67. pkey=None,
  68. key_filename=None,
  69. **kwargs,
  70. ):
  71. client = paramiko.SSHClient()
  72. connection_kwargs = {"hostname": host}
  73. connection_kwargs.update(self.kwargs)
  74. if username:
  75. connection_kwargs["username"] = username
  76. if port:
  77. connection_kwargs["port"] = port
  78. if password:
  79. connection_kwargs["password"] = password
  80. if pkey:
  81. connection_kwargs["pkey"] = pkey
  82. if key_filename:
  83. connection_kwargs["key_filename"] = key_filename
  84. connection_kwargs.update(kwargs)
  85. policy = paramiko.client.MissingHostKeyPolicy()
  86. client.set_missing_host_key_policy(policy)
  87. client.connect(**connection_kwargs)
  88. # Open SSH session
  89. channel = client.get_transport().open_session()
  90. # Run commands
  91. channel.exec_command(command)
  92. return _ParamikoWrapper(client, channel)