summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOlle Lundberg <geek@nerd.sh>2012-10-16 13:54:23 +0200
committerOlle Lundberg <geek@nerd.sh>2012-10-16 13:57:05 +0200
commitad587fa0ef32eeeb633245c7ae68759765c0e439 (patch)
tree6081670aa1b7608a2e6a4d1b0b4305494a50d077
parentf33481cc44fb57a788b5726b529383f300d06b36 (diff)
Add host negation support to paramiko config.
This is a rewrite of the SSHConfig class to conform with the rules specified by the manpage for ssh_config. This change also adds support for negation according to the rules introduced by OpenSSH 5.9. Reference: http://www.openssh.com/txt/release-5.9
-rw-r--r--paramiko/config.py80
1 files changed, 50 insertions, 30 deletions
diff --git a/paramiko/config.py b/paramiko/config.py
index 458d5dd0..802d9155 100644
--- a/paramiko/config.py
+++ b/paramiko/config.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com>
+# Copyright (C) 2012 Olle Lundberg <geek@nerd.sh>
#
# This file is part of paramiko.
#
@@ -41,7 +42,8 @@ class SSHConfig (object):
"""
Create a new OpenSSH config object.
"""
- self._config = [ { 'host': '*' } ]
+ self._config = []
+
def parse(self, file_obj):
"""
@@ -50,7 +52,7 @@ class SSHConfig (object):
@param file_obj: a file-like object to read the config file from
@type file_obj: file
"""
- configs = [self._config[0]]
+ host = {"host":['*'],"config":{}}
for line in file_obj:
line = line.rstrip('\n').lstrip()
if (line == '') or (line[0] == '#'):
@@ -69,20 +71,20 @@ class SSHConfig (object):
value = line[i:].lstrip()
if key == 'host':
- del configs[:]
- # the value may be multiple hosts, space-delimited
- for host in value.split():
- # do we have a pre-existing host config to append to?
- matches = [c for c in self._config if c['host'] == host]
- if len(matches) > 0:
- configs.append(matches[0])
- else:
- config = { 'host': host }
- self._config.append(config)
- configs.append(config)
- else:
- for config in configs:
- config[key] = value
+ self._config.append(host)
+ value = value.split()
+ host = {key:value,'config':{}}
+ #identitifile is a special case, since it is allowed to be
+ # specified multiple times and they should be tried in order
+ # of specification.
+ elif key == 'identityfile':
+ if key in host['config']:
+ host['config']['identityfile'].append(value)
+ else:
+ host['config']['identityfile'] = [value]
+ elif key not in host['config']:
+ host['config'].update({key:value})
+ self._config.append(host)
def lookup(self, hostname):
"""
@@ -97,31 +99,45 @@ class SSHConfig (object):
will win out.
The keys in the returned dict are all normalized to lowercase (look for
- C{"port"}, not C{"Port"}. No other processing is done to the keys or
- values.
+ C{"port"}, not C{"Port"}. The values are processed according to the
+ rules for substitution variable expansion in C{ssh_config}.
@param hostname: the hostname to lookup
@type hostname: str
"""
- matches = [x for x in self._config if fnmatch.fnmatch(hostname, x['host'])]
- # Move * to the end
- _star = matches.pop(0)
- matches.append(_star)
+
+ matches = [ config for config in self._config if
+ self._allowed(hostname,config['host']) ]
+
ret = {}
- for m in matches:
- for k,v in m.iteritems():
- if not k in ret:
- ret[k] = v
+ for match in matches:
+ for key in match['config']:
+ value = match['config'][key]
+ if key == 'identityfile':
+ if key in ret:
+ ret['identityfile'].extend(value)
+ else:
+ ret['identityfile'] = value
+ elif key not in ret:
+ ret[key] = value
ret = self._expand_variables(ret, hostname)
- del ret['host']
return ret
- def _expand_variables(self, config, hostname ):
+ def _allowed(self, hostname, hosts):
+ match = False
+ for host in hosts:
+ if host.startswith('!') and fnmatch.fnmatch(hostname, host[1:]):
+ return False
+ elif fnmatch.fnmatch(hostname, host):
+ match = True
+ return match
+
+ def _expand_variables(self, config, hostname):
"""
Return a dict of config options with expanded substitutions
for a given hostname.
- Please refer to man ssh_config(5) for the parameters that
+ Please refer to man C{ssh_config} for the parameters that
are replaced.
@param config: the config for the hostname
@@ -172,5 +188,9 @@ class SSHConfig (object):
for k in config:
if k in replacements:
for find, replace in replacements[k]:
- config[k] = config[k].replace(find, str(replace))
+ if isinstance(config[k],list):
+ for item in range(len(config[k])):
+ config[k][item] = config[k][item].replace(find, str(replace))
+ else:
+ config[k] = config[k].replace(find, str(replace))
return config