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 [email protected] 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 <[email protected]> # Copyright 2007 Kenneth Loafman <[email protected]> # Copyright 2011 Alexander Zangerl <[email protected]> +# 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)
_______________________________________________ Mailing list: https://launchpad.net/~duplicity-team Post to : [email protected] Unsubscribe : https://launchpad.net/~duplicity-team More help : https://help.launchpad.net/ListHelp

