comparison env/lib/python3.9/site-packages/boto/manage/cmdshell.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish, dis-
7 # tribute, sublicense, and/or sell copies of the Software, and to permit
8 # persons to whom the Software is furnished to do so, subject to the fol-
9 # lowing conditions:
10 #
11 # The above copyright notice and this permission notice shall be included
12 # in all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 # IN THE SOFTWARE.
21 """
22 The cmdshell module uses the paramiko package to create SSH connections
23 to the servers that are represented by instance objects. The module has
24 functions for running commands, managing files, and opening interactive
25 shell sessions over those connections.
26 """
27 from boto.mashups.interactive import interactive_shell
28 import boto
29 import os
30 import time
31 import shutil
32 import paramiko
33 import socket
34 import subprocess
35
36 from boto.compat import StringIO
37
38 class SSHClient(object):
39 """
40 This class creates a paramiko.SSHClient() object that represents
41 a session with an SSH server. You can use the SSHClient object to send
42 commands to the remote host and manipulate files on the remote host.
43
44 :ivar server: A Server object or FakeServer object.
45 :ivar host_key_file: The path to the user's .ssh key files.
46 :ivar uname: The username for the SSH connection. Default = 'root'.
47 :ivar timeout: The optional timeout variable for the TCP connection.
48 :ivar ssh_pwd: An optional password to use for authentication or for
49 unlocking the private key.
50 """
51 def __init__(self, server,
52 host_key_file='~/.ssh/known_hosts',
53 uname='root', timeout=None, ssh_pwd=None):
54 self.server = server
55 self.host_key_file = host_key_file
56 self.uname = uname
57 self._timeout = timeout
58 self._pkey = paramiko.RSAKey.from_private_key_file(server.ssh_key_file,
59 password=ssh_pwd)
60 self._ssh_client = paramiko.SSHClient()
61 self._ssh_client.load_system_host_keys()
62 self._ssh_client.load_host_keys(os.path.expanduser(host_key_file))
63 self._ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
64 self.connect()
65
66 def connect(self, num_retries=5):
67 """
68 Connect to an SSH server and authenticate with it.
69
70 :type num_retries: int
71 :param num_retries: The maximum number of connection attempts.
72 """
73 retry = 0
74 while retry < num_retries:
75 try:
76 self._ssh_client.connect(self.server.hostname,
77 username=self.uname,
78 pkey=self._pkey,
79 timeout=self._timeout)
80 return
81 except socket.error as xxx_todo_changeme:
82 (value, message) = xxx_todo_changeme.args
83 if value in (51, 61, 111):
84 print('SSH Connection refused, will retry in 5 seconds')
85 time.sleep(5)
86 retry += 1
87 else:
88 raise
89 except paramiko.BadHostKeyException:
90 print("%s has an entry in ~/.ssh/known_hosts and it doesn't match" % self.server.hostname)
91 print('Edit that file to remove the entry and then hit return to try again')
92 raw_input('Hit Enter when ready')
93 retry += 1
94 except EOFError:
95 print('Unexpected Error from SSH Connection, retry in 5 seconds')
96 time.sleep(5)
97 retry += 1
98 print('Could not establish SSH connection')
99
100 def open_sftp(self):
101 """
102 Open an SFTP session on the SSH server.
103
104 :rtype: :class:`paramiko.sftp_client.SFTPClient`
105 :return: An SFTP client object.
106 """
107 return self._ssh_client.open_sftp()
108
109 def get_file(self, src, dst):
110 """
111 Open an SFTP session on the remote host, and copy a file from
112 the remote host to the specified path on the local host.
113
114 :type src: string
115 :param src: The path to the target file on the remote host.
116
117 :type dst: string
118 :param dst: The path on your local host where you want to
119 store the file.
120 """
121 sftp_client = self.open_sftp()
122 sftp_client.get(src, dst)
123
124 def put_file(self, src, dst):
125 """
126 Open an SFTP session on the remote host, and copy a file from
127 the local host to the specified path on the remote host.
128
129 :type src: string
130 :param src: The path to the target file on your local host.
131
132 :type dst: string
133 :param dst: The path on the remote host where you want to store
134 the file.
135 """
136 sftp_client = self.open_sftp()
137 sftp_client.put(src, dst)
138
139 def open(self, filename, mode='r', bufsize=-1):
140 """
141 Open an SFTP session to the remote host, and open a file on
142 that host.
143
144 :type filename: string
145 :param filename: The path to the file on the remote host.
146
147 :type mode: string
148 :param mode: The file interaction mode.
149
150 :type bufsize: integer
151 :param bufsize: The file buffer size.
152
153 :rtype: :class:`paramiko.sftp_file.SFTPFile`
154 :return: A paramiko proxy object for a file on the remote server.
155 """
156 sftp_client = self.open_sftp()
157 return sftp_client.open(filename, mode, bufsize)
158
159 def listdir(self, path):
160 """
161 List all of the files and subdirectories at the specified path
162 on the remote host.
163
164 :type path: string
165 :param path: The base path from which to obtain the list.
166
167 :rtype: list
168 :return: A list of files and subdirectories at the specified path.
169 """
170 sftp_client = self.open_sftp()
171 return sftp_client.listdir(path)
172
173 def isdir(self, path):
174 """
175 Check the specified path on the remote host to determine if
176 it is a directory.
177
178 :type path: string
179 :param path: The path to the directory that you want to check.
180
181 :rtype: integer
182 :return: If the path is a directory, the function returns 1.
183 If the path is a file or an invalid path, the function
184 returns 0.
185 """
186 status = self.run('[ -d %s ] || echo "FALSE"' % path)
187 if status[1].startswith('FALSE'):
188 return 0
189 return 1
190
191 def exists(self, path):
192 """
193 Check the remote host for the specified path, or a file
194 at the specified path. This function returns 1 if the
195 path or the file exist on the remote host, and returns 0 if
196 the path or the file does not exist on the remote host.
197
198 :type path: string
199 :param path: The path to the directory or file that you want to check.
200
201 :rtype: integer
202 :return: If the path or the file exist, the function returns 1.
203 If the path or the file do not exist on the remote host,
204 the function returns 0.
205 """
206
207 status = self.run('[ -a %s ] || echo "FALSE"' % path)
208 if status[1].startswith('FALSE'):
209 return 0
210 return 1
211
212 def shell(self):
213 """
214 Start an interactive shell session with the remote host.
215 """
216 channel = self._ssh_client.invoke_shell()
217 interactive_shell(channel)
218
219 def run(self, command):
220 """
221 Run a command on the remote host.
222
223 :type command: string
224 :param command: The command that you want to send to the remote host.
225
226 :rtype: tuple
227 :return: This function returns a tuple that contains an integer status,
228 the stdout from the command, and the stderr from the command.
229
230 """
231 boto.log.debug('running:%s on %s' % (command, self.server.instance_id))
232 status = 0
233 try:
234 t = self._ssh_client.exec_command(command)
235 except paramiko.SSHException:
236 status = 1
237 std_out = t[1].read()
238 std_err = t[2].read()
239 t[0].close()
240 t[1].close()
241 t[2].close()
242 boto.log.debug('stdout: %s' % std_out)
243 boto.log.debug('stderr: %s' % std_err)
244 return (status, std_out, std_err)
245
246 def run_pty(self, command):
247 """
248 Request a pseudo-terminal from a server, and execute a command on that
249 server.
250
251 :type command: string
252 :param command: The command that you want to run on the remote host.
253
254 :rtype: :class:`paramiko.channel.Channel`
255 :return: An open channel object.
256 """
257 boto.log.debug('running:%s on %s' % (command, self.server.instance_id))
258 channel = self._ssh_client.get_transport().open_session()
259 channel.get_pty()
260 channel.exec_command(command)
261 return channel
262
263 def close(self):
264 """
265 Close an SSH session and any open channels that are tied to it.
266 """
267 transport = self._ssh_client.get_transport()
268 transport.close()
269 self.server.reset_cmdshell()
270
271 class LocalClient(object):
272 """
273 :ivar server: A Server object or FakeServer object.
274 :ivar host_key_file: The path to the user's .ssh key files.
275 :ivar uname: The username for the SSH connection. Default = 'root'.
276 """
277 def __init__(self, server, host_key_file=None, uname='root'):
278 self.server = server
279 self.host_key_file = host_key_file
280 self.uname = uname
281
282 def get_file(self, src, dst):
283 """
284 Copy a file from one directory to another.
285 """
286 shutil.copyfile(src, dst)
287
288 def put_file(self, src, dst):
289 """
290 Copy a file from one directory to another.
291 """
292 shutil.copyfile(src, dst)
293
294 def listdir(self, path):
295 """
296 List all of the files and subdirectories at the specified path.
297
298 :rtype: list
299 :return: Return a list containing the names of the entries
300 in the directory given by path.
301 """
302 return os.listdir(path)
303
304 def isdir(self, path):
305 """
306 Check the specified path to determine if it is a directory.
307
308 :rtype: boolean
309 :return: Returns True if the path is an existing directory.
310 """
311 return os.path.isdir(path)
312
313 def exists(self, path):
314 """
315 Check for the specified path, or check a file at the specified path.
316
317 :rtype: boolean
318 :return: If the path or the file exist, the function returns True.
319 """
320 return os.path.exists(path)
321
322 def shell(self):
323 raise NotImplementedError('shell not supported with LocalClient')
324
325 def run(self):
326 """
327 Open a subprocess and run a command on the local host.
328
329 :rtype: tuple
330 :return: This function returns a tuple that contains an integer status
331 and a string with the combined stdout and stderr output.
332 """
333 boto.log.info('running:%s' % self.command)
334 log_fp = StringIO()
335 process = subprocess.Popen(self.command, shell=True, stdin=subprocess.PIPE,
336 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
337 while process.poll() is None:
338 time.sleep(1)
339 t = process.communicate()
340 log_fp.write(t[0])
341 log_fp.write(t[1])
342 boto.log.info(log_fp.getvalue())
343 boto.log.info('output: %s' % log_fp.getvalue())
344 return (process.returncode, log_fp.getvalue())
345
346 def close(self):
347 pass
348
349 class FakeServer(object):
350 """
351 This object has a subset of the variables that are normally in a
352 :class:`boto.manage.server.Server` object. You can use this FakeServer
353 object to create a :class:`boto.manage.SSHClient` object if you
354 don't have a real Server object.
355
356 :ivar instance: A boto Instance object.
357 :ivar ssh_key_file: The path to the SSH key file.
358 """
359 def __init__(self, instance, ssh_key_file):
360 self.instance = instance
361 self.ssh_key_file = ssh_key_file
362 self.hostname = instance.dns_name
363 self.instance_id = self.instance.id
364
365 def start(server):
366 """
367 Connect to the specified server.
368
369 :return: If the server is local, the function returns a
370 :class:`boto.manage.cmdshell.LocalClient` object.
371 If the server is remote, the function returns a
372 :class:`boto.manage.cmdshell.SSHClient` object.
373 """
374 instance_id = boto.config.get('Instance', 'instance-id', None)
375 if instance_id == server.instance_id:
376 return LocalClient(server)
377 else:
378 return SSHClient(server)
379
380 def sshclient_from_instance(instance, ssh_key_file,
381 host_key_file='~/.ssh/known_hosts',
382 user_name='root', ssh_pwd=None):
383 """
384 Create and return an SSHClient object given an
385 instance object.
386
387 :type instance: :class`boto.ec2.instance.Instance` object
388 :param instance: The instance object.
389
390 :type ssh_key_file: string
391 :param ssh_key_file: A path to the private key file that is
392 used to log into the instance.
393
394 :type host_key_file: string
395 :param host_key_file: A path to the known_hosts file used
396 by the SSH client.
397 Defaults to ~/.ssh/known_hosts
398 :type user_name: string
399 :param user_name: The username to use when logging into
400 the instance. Defaults to root.
401
402 :type ssh_pwd: string
403 :param ssh_pwd: The passphrase, if any, associated with
404 private key.
405 """
406 s = FakeServer(instance, ssh_key_file)
407 return SSHClient(s, host_key_file, user_name, ssh_pwd)