Package paramiko :: Module config
[frames] | no frames]

Source Code for Module paramiko.config

  1  # Copyright (C) 2006-2007  Robey Pointer <robeypointer@gmail.com> 
  2  # 
  3  # This file is part of paramiko. 
  4  # 
  5  # Paramiko is free software; you can redistribute it and/or modify it under the 
  6  # terms of the GNU Lesser General Public License as published by the Free 
  7  # Software Foundation; either version 2.1 of the License, or (at your option) 
  8  # any later version. 
  9  # 
 10  # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 
 11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 13  # details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public License 
 16  # along with Paramiko; if not, write to the Free Software Foundation, Inc., 
 17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
 18   
 19  """ 
 20  L{SSHConfig}. 
 21  """ 
 22   
 23  import fnmatch 
 24  import os 
 25  import re 
 26  import socket 
 27   
 28  SSH_PORT=22 
 29  proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I) 
 30   
 31   
32 -class SSHConfig (object):
33 """ 34 Representation of config information as stored in the format used by 35 OpenSSH. Queries can be made via L{lookup}. The format is described in 36 OpenSSH's C{ssh_config} man page. This class is provided primarily as a 37 convenience to posix users (since the OpenSSH format is a de-facto 38 standard on posix) but should work fine on Windows too. 39 40 @since: 1.6 41 """ 42
43 - def __init__(self):
44 """ 45 Create a new OpenSSH config object. 46 """ 47 self._config = [ { 'host': '*' } ]
48
49 - def parse(self, file_obj):
50 """ 51 Read an OpenSSH config from the given file object. 52 53 @param file_obj: a file-like object to read the config file from 54 @type file_obj: file 55 """ 56 configs = [self._config[0]] 57 for line in file_obj: 58 line = line.rstrip('\n').lstrip() 59 if (line == '') or (line[0] == '#'): 60 continue 61 if '=' in line: 62 # Ensure ProxyCommand gets properly split 63 if line.lower().strip().startswith('proxycommand'): 64 match = proxy_re.match(line) 65 key, value = match.group(1).lower(), match.group(2) 66 else: 67 key, value = line.split('=', 1) 68 key = key.strip().lower() 69 else: 70 # find first whitespace, and split there 71 i = 0 72 while (i < len(line)) and not line[i].isspace(): 73 i += 1 74 if i == len(line): 75 raise Exception('Unparsable line: %r' % line) 76 key = line[:i].lower() 77 value = line[i:].lstrip() 78 79 if key == 'host': 80 del configs[:] 81 # the value may be multiple hosts, space-delimited 82 for host in value.split(): 83 # do we have a pre-existing host config to append to? 84 matches = [c for c in self._config if c['host'] == host] 85 if len(matches) > 0: 86 configs.append(matches[0]) 87 else: 88 config = { 'host': host } 89 self._config.append(config) 90 configs.append(config) 91 else: 92 for config in configs: 93 config[key] = value
94
95 - def lookup(self, hostname):
96 """ 97 Return a dict of config options for a given hostname. 98 99 The host-matching rules of OpenSSH's C{ssh_config} man page are used, 100 which means that all configuration options from matching host 101 specifications are merged, with more specific hostmasks taking 102 precedence. In other words, if C{"Port"} is set under C{"Host *"} 103 and also C{"Host *.example.com"}, and the lookup is for 104 C{"ssh.example.com"}, then the port entry for C{"Host *.example.com"} 105 will win out. 106 107 The keys in the returned dict are all normalized to lowercase (look for 108 C{"port"}, not C{"Port"}. No other processing is done to the keys or 109 values. 110 111 @param hostname: the hostname to lookup 112 @type hostname: str 113 """ 114 matches = [x for x in self._config if fnmatch.fnmatch(hostname, x['host'])] 115 # Move * to the end 116 _star = matches.pop(0) 117 matches.append(_star) 118 ret = {} 119 for m in matches: 120 for k,v in m.iteritems(): 121 if not k in ret: 122 ret[k] = v 123 ret = self._expand_variables(ret, hostname) 124 del ret['host'] 125 return ret
126
127 - def _expand_variables(self, config, hostname ):
128 """ 129 Return a dict of config options with expanded substitutions 130 for a given hostname. 131 132 Please refer to man ssh_config(5) for the parameters that 133 are replaced. 134 135 @param config: the config for the hostname 136 @type hostname: dict 137 @param hostname: the hostname that the config belongs to 138 @type hostname: str 139 """ 140 141 if 'hostname' in config: 142 config['hostname'] = config['hostname'].replace('%h',hostname) 143 else: 144 config['hostname'] = hostname 145 146 if 'port' in config: 147 port = config['port'] 148 else: 149 port = SSH_PORT 150 151 user = os.getenv('USER') 152 if 'user' in config: 153 remoteuser = config['user'] 154 else: 155 remoteuser = user 156 157 host = socket.gethostname().split('.')[0] 158 fqdn = socket.getfqdn() 159 homedir = os.path.expanduser('~') 160 replacements = { 161 'controlpath': [ 162 ('%h', config['hostname']), 163 ('%l', fqdn), 164 ('%L', host), 165 ('%n', hostname), 166 ('%p', port), 167 ('%r', remoteuser), 168 ('%u', user) 169 ], 170 'identityfile': [ 171 ('~', homedir), 172 ('%d', homedir), 173 ('%h', config['hostname']), 174 ('%l', fqdn), 175 ('%u', user), 176 ('%r', remoteuser) 177 ], 178 'proxycommand': [ 179 ('%h', config['hostname']), 180 ('%p', port), 181 ('%r', remoteuser), 182 ], 183 } 184 for k in config: 185 if k in replacements: 186 for find, replace in replacements[k]: 187 config[k] = config[k].replace(find, str(replace)) 188 return config
189