1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
|
#
# Copyright (C) 2002 Manuel Estrada Sainz <ranty@debian.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from apt_proxy import Backend
from misc import log
import os, sys, re
from types import StringType, NoneType
import urlparse
from ConfigParser import RawConfigParser,DEFAULTSECT
class ConfigError(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return repr(self.message)
class apConfigParser(RawConfigParser):
"""
Adds 'gettime' to ConfigParser to interpret the suffixes.
Interprets 'disabled_keyword' as disabled (None).
"""
time_multipliers={
's': 1, #seconds
'm': 60, #minutes
'h': 3600, #hours
'd': 86400,#days
}
DISABLED_KEYWORD = 'off'
def isOff(self, section, option):
value = self.get(section, option)
return value == self.DISABLED_KEYWORD
def getint(self, section, option):
value = self.get(section, option)
return int(value)
def gettime(self, section, option):
mult = 1
value = self.get(section, option)
if len(value) == 0:
raise ConfigError("Configuration parse error: [%s] %s" % (section, option))
suffix = value[-1].lower()
if suffix in self.time_multipliers.keys():
mult = self.time_multipliers[suffix]
value = value[:-1]
return int(value)*mult
def getstring(self, section, option):
return self.get(section,option)
def getstringlist(self, section, option):
return self.get(section,option).split()
def getproxyspec(self, section, option):
"Get http proxy info from string"
p = ProxyConfig(self.get(section,option))
if p.host is not None:
return p
else:
return None
class apConfig:
"""
Configuration module for apt-proxy
holds main configuration values in class and backend
configs in backends[backend-name]
"""
"""
Configuration items for default section
[0] - name of config parameter and resulting class variable name
[1] - default value
[2] - getXXX method to use to read config.
A method prefixed with '*' will return None if disabled
"""
CONFIG_ITEMS = [
['address', '', 'string'],
['port', 9999, 'int'],
['min_refresh_delay', 30, 'time'],
['complete_clientless_downloads', False, 'boolean'],
['telnet_port', 0, 'int'],
['telnet_user', '', 'string'],
['telnet_pass', '', 'string'],
['timeout', 30, 'time'],
['cleanup_freq', 600, '*time'],
['cache_dir', '/var/cache/apt-proxy', 'string'],
['max_versions', 3, '*int'],
['max_age', 10, '*time'],
['import_dir', '/var/cache/apt-proxy/import', 'string'],
['passive_ftp', 'on', 'boolean'],
['dynamic_backends', 'on', 'boolean'],
['http_proxy', None , 'proxyspec'],
['username', 'aptproxy', 'string'],
['bandwidth_limit', None, '*int']
]
"""
Configuration items for backends
[0] - name of config parameter and resulting class variable name
[1] - default value, None to use factory default
[2] - getXXX method to use to read config.
A method prefixed with '*' will return None if disabled
"""
BACKEND_CONFIG_ITEMS = [
['timeout', None, 'time'],
['passive_ftp', None, 'boolean'],
['backends', '', 'stringlist'],
['http_proxy', None , 'proxyspec'],
['bandwidth_limit', None, '*int']
]
DEFAULT_CONFIG_FILE = ['/etc/apt-proxy/apt-proxy-v2.conf',
'/etc/apt-proxy/apt-proxy-2.conf',
'/etc/apt-proxy/apt-proxy.conf']
"Backend configurations are held here"
backends = {}
parser = None
debugDomains = {}
debug = '0'
def __init__(self, config_file = None):
"""
Read configuration from specified source, or default config
file location if not specified
@param config_file Filename to read, or descriptor of already-open file
"""
self.backends = {}
if type(config_file) is StringType or type(config_file) is NoneType:
c = self.readFromFile(config_file)
else:
c = self.readFromStream(config_file)
self.parseConfig(c)
def readFromFile(self, config_file):
"""
Read configuration from filename given
@param config_file filename to read from, or None for default
"""
conf = apConfigParser()
if config_file is not None:
config_files = config_file,
else:
config_files = self.DEFAULT_CONFIG_FILE
for f in config_files:
if os.path.exists(f):
conf.read(f)
break
else:
raise ConfigError("%s: Configuration file does not exist" % config_files[0])
return conf
def readFromStream(self, filehandle):
"Read from open file handle, for test suite"
conf = apConfigParser()
conf.readfp(filehandle)
filehandle.close()
return conf
def setDebug(self):
"Set logger debug level"
for domain in self.debug.split():
#print "domain:",domain
if domain.find(':') != -1:
name, level = domain.split(':')
else:
name, level = domain, 9
self.debugDomains[name] = int(level)
log.setDomains(self.debugDomains)
def parseConfig(self, config):
"Read values from apConfigParser config"
# debug setting
if config.has_option(DEFAULTSECT, 'debug'):
self.debug=config.get(DEFAULTSECT, 'debug')
else:
self.debug='all:3'
self.setDebug()
# read default values
for name,default,getmethod in self.CONFIG_ITEMS:
value = self.parseConfigValue(config, DEFAULTSECT, name, default, getmethod)
setattr(self, name, value)
if value != default and name != "telnet_pass":
log.debug("config value %s=%s"%(name, value), 'config')
self.address = self.address.split(" ")
if not self.telnet_user or not self.telnet_pass:
self.telnet_port = 0 # No server if empty username or password
# Read backend configurations
for backendName in config.sections():
self.addBackend(config, backendName)
def addBackend(self, config, backendName, backendServers=None):
"""
Add a new backend configuration
@param config Configuration file parser to get backend values from. If None, backend is dynamic
@param backendName Name of backend to create
@param backendServers List of backend servers to use (if backend is dynamic)
@ret newly created apBackendConfig
"""
if backendName.find('/') != -1:
log.msg("WARNING: backend %s contains '/' (ignored)"%(name))
return None
backend = apBackendConfig(backendName)
for paramName,default,getmethod in self.BACKEND_CONFIG_ITEMS:
if default is None:
default = getattr(self, paramName) # Use default section's default value
value = self.parseConfigValue(config, backendName, paramName, default, getmethod)
setattr(backend,paramName, value)
if value != default:
log.debug("[backend %s] %s=%s"%(backendName, paramName, value), 'config')
if backendServers is None:
backend.dynamic = False
backendServers = backend.backends
else:
# Dynamic backend
backend.dynamic = True
backend.backends = []
for server in backendServers:
if server[-1] == '/':
log.msg ("Removing unnecessary '/' at the end of %s"%(server))
server = server[0:-1]
if urlparse.urlparse(server)[0] in ['http', 'ftp', 'rsync', 'file']:
backend.backends.append(server)
else:
log.msg ("WARNING: Wrong server '%s' found in backend '%s'. It was skipped." % (server, backendName))
return None
if len(backend.backends) == 0:
log.msg("WARNING: [%s] has no backend servers (ignored)"%backendName)
return None
self.backends[backendName] = backend
return backend
def parseConfigValue(self, config, section, name, default, getmethod):
"Determine value of given config item"
if config is None or not config.has_option(section, name):
return default
if getmethod[0]=='*':
if config.isOff(section, name):
return None
else:
return getattr(config, 'get'+getmethod[1:])(section, name)
else:
return getattr(config, 'get'+getmethod)(section, name)
class apBackendConfig:
"""
Configuration information for an apt-proxy backend
"""
name = "UNKNOWN"
def __init__(self, name):
self.name = name
class ProxyConfig:
"""
Configuration information for backend server proxies
"""
host = None
port = None
user = None
password = None
def __init__(self, proxyspec):
if proxyspec=='':
return
m = re.match('^((?P<user>.*):(?P<password>.*)@)?(?P<host>[a-zA-Z0-9_.+=-]+):(?P<port>[a-zA-Z0-9]+)',
proxyspec)
if m:
self.host = m.group('host')
self.port = m.group('port')
try:
self.port = int(self.port)
except ValueError:
pass
self.user = m.group('user')
self.password = m.group('password')
|