1
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
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-
11
# The above copyright notice and this permission notice shall be included
12
# in all copies or substantial portions of the Software.
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
22
from boto.mashups.interactive import interactive_shell
33
class SSHClient(object):
35
def __init__(self, server, host_key_file='~/.ssh/known_hosts', uname='root'):
37
self.host_key_file = host_key_file
39
self._pkey = paramiko.RSAKey.from_private_key_file(server.ssh_key_file)
40
self._ssh_client = paramiko.SSHClient()
41
self._ssh_client.load_system_host_keys()
42
self._ssh_client.load_host_keys(os.path.expanduser(host_key_file))
43
self._ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
50
self._ssh_client.connect(self.server.hostname, username=self.uname, pkey=self._pkey)
52
except socket.error, (value,message):
54
print 'SSH Connection refused, will retry in 5 seconds'
59
except paramiko.BadHostKeyException:
60
print "%s has an entry in ~/.ssh/known_hosts and it doesn't match" % self.server.hostname
61
print 'Edit that file to remove the entry and then hit return to try again'
62
raw_input('Hit Enter when ready')
65
print 'Unexpected Error from SSH Connection, retry in 5 seconds'
68
print 'Could not establish SSH connection'
70
def get_file(self, src, dst):
71
sftp_client = self._ssh_client.open_sftp()
72
sftp_client.get(src, dst)
74
def put_file(self, src, dst):
75
sftp_client = self._ssh_client.open_sftp()
76
sftp_client.put(src, dst)
78
def listdir(self, path):
79
sftp_client = self._ssh_client.open_sftp()
80
return sftp_client.listdir(path)
83
return self._ssh_client.open_sftp()
85
def isdir(self, path):
86
status = self.run('[ -d %s ] || echo "FALSE"' % path)
87
if status[1].startswith('FALSE'):
91
def exists(self, path):
92
status = self.run('[ -a %s ] || echo "FALSE"' % path)
93
if status[1].startswith('FALSE'):
98
channel = self._ssh_client.invoke_shell()
99
interactive_shell(channel)
101
def run(self, command):
102
boto.log.info('running:%s on %s' % (command, self.server.instance_id))
103
log_fp = StringIO.StringIO()
106
t = self._ssh_client.exec_command(command)
107
except paramiko.SSHException:
109
log_fp.write(t[1].read())
110
log_fp.write(t[2].read())
114
boto.log.info('output: %s' % log_fp.getvalue())
115
return (status, log_fp.getvalue())
118
transport = self._ssh_client.get_transport()
120
self.server.reset_cmdshell()
122
class LocalClient(object):
124
def __init__(self, server, host_key_file=None, uname='root'):
126
self.host_key_file = host_key_file
129
def get_file(self, src, dst):
130
shutil.copyfile(src, dst)
132
def put_file(self, src, dst):
133
shutil.copyfile(src, dst)
135
def listdir(self, path):
136
return os.listdir(path)
138
def isdir(self, path):
139
return os.path.isdir(path)
141
def exists(self, path):
142
return os.path.exists(path)
145
raise NotImplementedError, 'shell not supported with LocalClient'
148
boto.log.info('running:%s' % self.command)
149
log_fp = StringIO.StringIO()
150
process = subprocess.Popen(self.command, shell=True, stdin=subprocess.PIPE,
151
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
152
while process.poll() == None:
154
t = process.communicate()
157
boto.log.info(log_fp.getvalue())
158
boto.log.info('output: %s' % log_fp.getvalue())
159
return (process.returncode, log_fp.getvalue())
165
instance_id = boto.config.get('Instance', 'instance-id', None)
166
if instance_id == server.instance_id:
167
return LocalClient(server)
169
return SSHClient(server)