1 # vim: ai ts=4 sts=4 et sw=4
2 from paramiko import Transport, SFTPClient, RSAKey, DSSKey
3 from paramiko.config import SSHConfig
5 from cumulus.store import Store, type_patterns, NotFoundError
10 #needed for python 2.5
12 from __future__ import with_statement
13 except ImportError, e:
14 raise ImportError('We need the "with" statement, avalible with python >=2.5')
17 class SSHHostConfig(dict):
18 def __init__(self, hostname, user = None, filename = None):
22 filename = os.path.expanduser('~/.ssh/config')
24 self['user'] = getpass.getuser()
25 self['hostname'] = hostname
26 self['hostkeyalias'] = hostname
29 ssh_config = SSHConfig()
30 with open(filename) as config_file:
31 ssh_config.parse(config_file)
32 self.update(ssh_config.lookup(hostname))
37 class SFTPStore(Store):
38 """implements the sftp:// storage backend
40 configuration via openssh/sftp style urls and
43 does not support password authentication or password
44 protected authentication keys"""
45 def __init__(self, url, **kw):
46 if self.path.find('@') != -1:
47 user, self.path = self.path.split('@')
51 if self.path.find(':') != -1:
52 host, self.path = selfpath.split(':')
54 host, self.path = self.path.split('/', 1)
56 self.config = SSHHostConfig(host, user)
59 host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
60 self.hostkey = host_keys[config['hostkeyalias']].values()[0]
62 if(config.has_key('identityfile')):
63 key_file = os.path.expanduser(host_config['identityfile'])
64 #not really nice but i don't see a cleaner way atm...
66 self.auth_key = RSAKey (filename = key_file)
67 except SSHException, e:
68 if e.message == 'Unable to parse file':
69 self.auth_key = DSAKey (filename = key_file)
73 filename = os.path.expanduser('~/.ssh/id_rsa')
74 if os.path.exists(filename):
75 self.auth_key = RSAKey(filename = filename)
77 filename = os.path.expanduser('~/.ssh/id_dsa')
78 if (os.path.exists(filename)):
79 self.auth_key = DSSKey (filename = filename)
84 self.t = Transport((self.config['hostname'], self.config['port']))
85 self.t.connect(username = self.config['user'], pkey = self.auth_key)
86 self.client = SFTPClient.from_transport(self.t)
87 self.client.chdir(self.path)
89 def __build_fn(self, name):
90 return "%s/%s" % (self.path, name)
93 return filter(type_patterns[type].match, self.client.listdir(self.path))
95 def get(self, type, name):
96 return self.client.open(filename = self.__build_fn(name), mode = 'rb')
98 def put(self, type, name, fp):
99 remote_file = self.client.open(filename = self.__build_fn(name), mode = 'wb')
101 while (len(buf) > 0):
102 remote_file.write(buf)
106 def delete(self, type, name):
107 self.client.remove(filename = self.__build_fn(name))
109 def stat(self, type, name):
110 stat = self.client.stat(filename = self.__build_fn(name))
111 return {'size': stat.st_size}
114 """connection has to be explicitly closed, otherwise
115 it will hold the process running idefinitly"""