duplicity-team team mailing list archive
-
duplicity-team team
-
Mailing list archive
-
Message #01092
[Merge] lp:~ed.so/duplicity/0.6-ssh_config into lp:duplicity
edso has proposed merging lp:~ed.so/duplicity/0.6-ssh_config into lp:duplicity.
Requested reviews:
duplicity-team (duplicity-team)
For more details, see:
https://code.launchpad.net/~ed.so/duplicity/0.6-ssh_config/+merge/96578
--
https://code.launchpad.net/~ed.so/duplicity/0.6-ssh_config/+merge/96578
Your team duplicity-team is requested to review the proposed merge of lp:~ed.so/duplicity/0.6-ssh_config into lp:duplicity.
=== modified file 'Changelog.GNU'
--- Changelog.GNU 2012-02-29 15:00:33 +0000
+++ Changelog.GNU 2012-03-08 14:15:22 +0000
@@ -1,3 +1,8 @@
+2012-03-08 edso
+ add ssh_config support (/etc/ssh/ssh_config + ~/.ssh/config) to paramiko sshbackend
+ @Ken: would you please announce that sshbackend is paramiko based native python now in the Changelog for the next release?
+ this was missing in 0.6.18's Changelog
+
2012-02-29 kenneth@xxxxxxxxxxx
Changes for 0.6.18.
=== modified file 'duplicity/backends/sshbackend.py'
--- duplicity/backends/sshbackend.py 2012-02-29 16:40:41 +0000
+++ duplicity/backends/sshbackend.py 2012-03-08 14:15:22 +0000
@@ -3,6 +3,7 @@
# Copyright 2002 Ben Escoto <ben@xxxxxxxxxxx>
# Copyright 2007 Kenneth Loafman <kenneth@xxxxxxxxxxx>
# Copyright 2011 Alexander Zangerl <az@xxxxxxxxxxxxx>
+# Copyright 2012 edso (ssh_config added)
#
# $Id: sshbackend.py,v 1.2 2011/12/31 04:44:12 az Exp $
#
@@ -62,27 +63,12 @@
def __init__(self, parsed_url):
duplicity.backend.Backend.__init__(self, parsed_url)
- # host string could be [user@]hostname
- if parsed_url.username:
- username=parsed_url.username
- else:
- username=getpass.getuser()
-
if parsed_url.path:
# remove first leading '/'
self.remote_dir = re.sub(r'^/', r'', parsed_url.path, 1)
else:
self.remote_dir = '.'
-
- # set up password
- if globals.ssh_askpass:
- password = self.get_password()
- else:
- if parsed_url.password:
- password = parsed_url.password
- else:
- password = None
self.client = paramiko.SSHClient()
# load known_hosts files
# paramiko is very picky wrt format and bails out on any problem...
@@ -96,23 +82,64 @@
except Exception, e:
raise BackendException("could not load ~/.ssh/known_hosts, maybe corrupt?")
- # alternative ssh private key?
- keyfilename=None
+ """ the next block reorganizes all host parameters into a
+ dictionary like SSHConfig does. this dictionary 'self.config'
+ becomes the authorative source for these values from here on.
+ rationale is that it is easiest to deal wrt overwriting multiple
+ values from ssh_config file. (ede 03/2012)
+ """
+ self.config={'hostname':parsed_url.hostname}
+ # get system host config entries
+ self.config.update(self.gethostconfig('/etc/ssh/ssh_config',parsed_url.hostname))
+ # update with user's config file
+ self.config.update(self.gethostconfig('~/.ssh/config',parsed_url.hostname))
+ # update with url values
+ ## username from url
+ if parsed_url.username:
+ self.config.update({'user':parsed_url.username})
+ ## username from input
+ if not 'user' in self.config:
+ self.config.update({'user':getpass.getuser()})
+ ## port from url
+ if parsed_url.port:
+ self.config.update({'port':parsed_url.port})
+ ## ensure there is deafult 22 or an int value
+ if 'port' in self.config:
+ self.config.update({'port':int(self.config['port'])})
+ else:
+ self.config.update({'port':22})
+ ## alternative ssh private key, identity file
m=re.search("-oidentityfile=(\S+)",globals.ssh_options,re.I)
if (m!=None):
keyfilename=m.group(1)
-
- if parsed_url.port:
- portnumber=parsed_url.port
+ self.config['identityfile'] = keyfilename
+ ## ensure ~ is expanded and identity exists in dictionary
+ if 'identityfile' in self.config:
+ self.config['identityfile'] = os.path.expanduser(
+ self.config['identityfile'])
else:
- portnumber=22
+ self.config['identityfile'] = None
+
+ # get password, enable prompt if askpass is set
+ self.use_getpass = globals.ssh_askpass
+ ## set url values for beautiful login prompt
+ parsed_url.username = self.config['user']
+ parsed_url.hostname = self.config['hostname']
+ password = self.get_password()
+
try:
- self.client.connect(hostname=parsed_url.hostname, port=portnumber,
- username=username, password=password,
- allow_agent=True, look_for_keys=True,
- key_filename=keyfilename)
+ self.client.connect(hostname=self.config['hostname'],
+ port=self.config['port'],
+ username=self.config['user'],
+ password=password,
+ allow_agent=True,
+ look_for_keys=True,
+ key_filename=self.config['identityfile'])
except Exception, e:
- raise BackendException("ssh connection to %s:%d failed: %s" % (parsed_url.hostname,portnumber,e))
+ raise BackendException("ssh connection to %s@%s:%d failed: %s" % (
+ self.config['user'],
+ self.config['hostname'],
+ self.config['port'],e))
self.client.get_transport().set_keepalive((int)(globals.timeout / 2))
# scp or sftp?
@@ -279,6 +306,21 @@
raise BackendException("%sfailed(%d): %s" % (errorprefix,res,chan.recv_stderr(4096)))
return output
+ def gethostconfig(self, file, host):
+ file = os.path.expanduser(file)
+ if not os.path.isfile(file):
+ return {}
+
+ sshconfig = paramiko.SSHConfig()
+ try:
+ sshconfig.parse(open(file))
+ except Exception, e:
+ raise BackendException("could not load '%s', maybe corrupt?" % (file))
+
+ return sshconfig.lookup(host)
+
+
+
duplicity.backend.register_backend("sftp", SftpBackend)
duplicity.backend.register_backend("scp", SftpBackend)
duplicity.backend.register_backend("ssh", SftpBackend)
Follow ups