2006-10-10 16:04:34 +02:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2006-10-12 15:22:56 +02:00
|
|
|
# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved.
|
2006-10-10 16:04:34 +02:00
|
|
|
# This program is free software; it may be used, copied, modified
|
|
|
|
# and distributed under the terms of the GNU General Public Licence,
|
|
|
|
# either version 2, or (at your option) any later version.
|
|
|
|
|
|
|
|
"""Read osc configuration and store it in a dictionary
|
|
|
|
|
|
|
|
This module reads and parses ~/.oscrc. The resulting configuration is stored
|
|
|
|
for later usage in a dictionary named 'config'.
|
|
|
|
|
|
|
|
In the absence of .oscrc, it tries .netrc.
|
|
|
|
If information is missing, it asks the user questions.
|
|
|
|
|
|
|
|
After reading the config, urllib2 is initialized.
|
|
|
|
|
|
|
|
The configuration dictionary could look like this:
|
|
|
|
|
2007-04-25 12:24:51 +02:00
|
|
|
{'apisrv': 'https://api.opensuse.org/',
|
2006-10-10 16:04:34 +02:00
|
|
|
'user': 'poeml',
|
|
|
|
'auth_dict': {'api.opensuse.org': {'user': 'poeml', 'pass': 'secret'},
|
|
|
|
'apitest.opensuse.org': {'user': 'poeml', 'pass': 'secret'},
|
|
|
|
'foo.opensuse.org': {'user': 'foo', 'pass': 'foo'}},
|
|
|
|
'build-cmd': '/usr/bin/build',
|
|
|
|
'build-root': '/abuild/oscbuild-%(repo)s-%(arch)s',
|
|
|
|
'packagecachedir': '/var/cache/osbuild',
|
|
|
|
'su-wrapper': 'sudo',
|
2007-07-16 11:45:17 +02:00
|
|
|
'urllist': ['http://download.opensuse.org/repositories/%(project)s/%(repository)s/%(arch)s/%(filename)s',
|
2006-10-10 16:04:34 +02:00
|
|
|
'http://api.opensuse.org/rpm/%(project)s/%(repository)s/_repository/%(buildarch)s/%(name)s'],
|
|
|
|
}
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
import ConfigParser
|
|
|
|
|
|
|
|
# being global to this module, this dict can be accessed from outside
|
|
|
|
# it will hold the parsed configuration
|
|
|
|
config = { }
|
|
|
|
|
2007-04-25 12:24:51 +02:00
|
|
|
DEFAULTS = { 'apisrv': 'https://api.opensuse.org/',
|
2006-10-13 11:19:28 +02:00
|
|
|
'scheme': 'https',
|
2006-10-10 16:04:34 +02:00
|
|
|
'user': 'your_username',
|
|
|
|
'packagecachedir': '/var/tmp/osbuild-packagecache',
|
|
|
|
'su-wrapper': 'su -c',
|
|
|
|
'build-cmd': '/usr/bin/build',
|
|
|
|
'build-root': '/var/tmp/build-root',
|
|
|
|
|
|
|
|
# default list of download URLs, which will be tried in order
|
|
|
|
'urllist': [
|
|
|
|
# the normal repo server, redirecting to mirrors
|
2007-07-16 11:45:17 +02:00
|
|
|
'http://download.opensuse.org/repositories/%(project)s/%(repository)s/%(arch)s/%(filename)s',
|
2006-10-10 16:04:34 +02:00
|
|
|
# direct access to "full" tree
|
2007-05-07 22:14:20 +02:00
|
|
|
'%(scheme)s://%(apisrv)s/build/%(project)s/%(repository)s/%(buildarch)s/_repository/%(name)s',
|
2006-10-10 16:04:34 +02:00
|
|
|
],
|
2006-10-12 15:22:56 +02:00
|
|
|
|
2007-04-19 10:47:22 +02:00
|
|
|
'http_debug': '0',
|
2007-04-19 12:40:18 +02:00
|
|
|
'cookiejar': '~/.osc_cookiejar',
|
2008-03-10 19:04:23 +01:00
|
|
|
# disable project tracking by default
|
|
|
|
'do_package_tracking': '0',
|
2006-10-10 16:04:34 +02:00
|
|
|
}
|
2008-03-10 19:04:23 +01:00
|
|
|
boolean_opts = ['http_debug', 'do_package_tracking']
|
2006-10-10 16:04:34 +02:00
|
|
|
|
|
|
|
new_conf_template = """
|
|
|
|
[general]
|
|
|
|
|
2007-04-25 12:24:51 +02:00
|
|
|
# URL to access API server, e.g. %(apisrv)s
|
|
|
|
# you also need a section [%(apisrv)s] with the credentials
|
|
|
|
#apisrv = %(apisrv)s
|
|
|
|
|
2006-10-10 16:04:34 +02:00
|
|
|
# Downloaded packages are cached here. Must be writable by you.
|
|
|
|
#packagecachedir = %(packagecachedir)s
|
|
|
|
|
|
|
|
# Wrapper to call build as root (sudo, su -, ...)
|
|
|
|
#su-wrapper = %(su-wrapper)s
|
|
|
|
|
|
|
|
# rootdir to setup the chroot environment
|
2007-04-18 02:26:14 +02:00
|
|
|
# can contain %%(repo)s and/or %%(arch)s for replacement, e.g.
|
|
|
|
# /srv/oscbuild/%%(repo)s-%%(arch)s
|
2006-10-10 16:04:34 +02:00
|
|
|
#build-root = %(build-root)s
|
|
|
|
|
2007-04-19 10:47:22 +02:00
|
|
|
# show HTTP traffic useful for debugging
|
2007-04-25 12:24:51 +02:00
|
|
|
#http_debug = 1
|
2007-04-19 10:47:22 +02:00
|
|
|
|
2006-10-10 16:04:34 +02:00
|
|
|
[%(apisrv)s]
|
|
|
|
user = %(user)s
|
|
|
|
pass = %(pass)s
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
account_not_configured_text ="""
|
|
|
|
Your user account / password are not configured yet.
|
|
|
|
You will be asked for them below, and they will be stored in
|
|
|
|
%s for future use.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
config_incomplete_text = """
|
|
|
|
|
|
|
|
Your configuration file %s is not complete.
|
|
|
|
Make sure that it has a [general] section.
|
|
|
|
(You can copy&paste the below. Some commented defaults are shown.)
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2007-04-19 12:40:18 +02:00
|
|
|
cookiejar = None
|
2006-10-10 16:04:34 +02:00
|
|
|
|
2007-04-25 12:24:51 +02:00
|
|
|
def parse_apisrv_url(scheme, apisrv):
|
|
|
|
import urlparse
|
|
|
|
if apisrv.startswith('http://') or apisrv.startswith('https://'):
|
|
|
|
return urlparse.urlsplit(apisrv)[0:2]
|
|
|
|
else:
|
|
|
|
return scheme, apisrv
|
|
|
|
|
2008-03-15 23:51:37 +01:00
|
|
|
def get_apiurl_usr(apiurl):
|
|
|
|
"""
|
|
|
|
returns the user for this host - if this host does not exist in the
|
|
|
|
configfile the default user is returned.
|
|
|
|
"""
|
|
|
|
import sys
|
|
|
|
scheme, apisrv = parse_apisrv_url(None, apiurl)
|
|
|
|
cp = get_configParser()
|
|
|
|
try:
|
|
|
|
return cp.get(apisrv, 'user')
|
|
|
|
except ConfigParser.NoSectionError:
|
|
|
|
print >>sys.stderr, 'section [\'%s\'] does not exist - using default user: \'%s\'' \
|
|
|
|
% (apisrv, config['user'])
|
|
|
|
return config['user']
|
|
|
|
|
2006-10-10 16:04:34 +02:00
|
|
|
def init_basicauth(config):
|
|
|
|
"""initialize urllib2 with the credentials for Basic Authentication"""
|
2007-04-19 10:47:22 +02:00
|
|
|
|
2006-10-10 16:04:34 +02:00
|
|
|
from osc.core import __version__
|
2007-04-19 12:40:18 +02:00
|
|
|
import os, urllib2
|
|
|
|
import cookielib
|
|
|
|
|
|
|
|
global cookiejar
|
2006-10-10 16:04:34 +02:00
|
|
|
|
2007-08-08 15:24:52 +02:00
|
|
|
# HTTPS proxy is not supported by urllib2. It only leads to an error
|
|
|
|
# or, at best, a warning.
|
|
|
|
# https://bugzilla.novell.com/show_bug.cgi?id=214983
|
|
|
|
# https://bugzilla.novell.com/show_bug.cgi?id=298378
|
|
|
|
if 'https_proxy' in os.environ:
|
|
|
|
del os.environ['https_proxy']
|
|
|
|
if 'HTTPS_PROXY' in os.environ:
|
|
|
|
del os.environ['HTTPS_PROXY']
|
|
|
|
|
2007-04-25 01:00:12 +02:00
|
|
|
if config['http_debug']:
|
2007-04-19 10:47:22 +02:00
|
|
|
# brute force
|
|
|
|
def urllib2_debug_init(self, debuglevel=0):
|
|
|
|
self._debuglevel = 1
|
|
|
|
urllib2.AbstractHTTPHandler.__init__ = urllib2_debug_init
|
2006-10-10 16:04:34 +02:00
|
|
|
|
2007-04-19 10:47:22 +02:00
|
|
|
authhandler = urllib2.HTTPBasicAuthHandler( \
|
|
|
|
urllib2.HTTPPasswordMgrWithDefaultRealm())
|
2006-10-10 16:04:34 +02:00
|
|
|
|
2007-04-19 12:40:18 +02:00
|
|
|
cookie_file = os.path.expanduser(config['cookiejar'])
|
|
|
|
cookiejar = cookielib.LWPCookieJar(cookie_file)
|
|
|
|
try:
|
|
|
|
cookiejar.load(ignore_discard=True)
|
|
|
|
except IOError:
|
|
|
|
try:
|
|
|
|
open(cookie_file, 'w').close()
|
|
|
|
os.chmod(cookie_file, 0600)
|
|
|
|
except:
|
|
|
|
#print 'Unable to create cookiejar file: \'%s\'. Using RAM-based cookies.' % cookie_file
|
|
|
|
cookiejar = cookielib.CookieJar()
|
|
|
|
|
|
|
|
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar), authhandler)
|
2006-10-10 16:04:34 +02:00
|
|
|
urllib2.install_opener(opener)
|
|
|
|
|
2007-04-19 10:47:22 +02:00
|
|
|
opener.addheaders = [('User-agent', 'osc/%s' % __version__)]
|
|
|
|
|
|
|
|
# with None as first argument, it will always use this username/password
|
|
|
|
# combination for urls for which arg2 (apisrv) is a super-url
|
2007-04-25 10:56:31 +02:00
|
|
|
for host, auth in config['auth_dict'].iteritems():
|
|
|
|
authhandler.add_password(None, host, auth['user'], auth['pass'])
|
2006-10-10 16:04:34 +02:00
|
|
|
|
|
|
|
|
2008-03-15 23:51:37 +01:00
|
|
|
def get_configParser(conffile=None, force_read=False):
|
|
|
|
"""
|
|
|
|
Returns an ConfigParser() object. After its first invocation the
|
|
|
|
ConfigParser object is stored in a method attribute and this attribute
|
|
|
|
is returned unless you pass force_read=True.
|
|
|
|
"""
|
|
|
|
import os
|
|
|
|
conffile = conffile or os.environ.get('OSC_CONFIG', '~/.oscrc')
|
|
|
|
conffile = os.path.expanduser(conffile)
|
|
|
|
if force_read or not get_configParser.__dict__.has_key('cp'):
|
|
|
|
get_configParser.cp = ConfigParser.SafeConfigParser(DEFAULTS)
|
|
|
|
get_configParser.cp.read(conffile)
|
|
|
|
return get_configParser.cp
|
|
|
|
|
|
|
|
|
2007-08-22 12:18:25 +02:00
|
|
|
def get_config(override_conffile = None,
|
|
|
|
override_http_debug = None,
|
|
|
|
override_apisrv = None):
|
2006-10-10 16:04:34 +02:00
|
|
|
"""do the actual work (see module documentation)"""
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
global config
|
|
|
|
|
2007-08-22 12:18:25 +02:00
|
|
|
conffile = override_conffile or os.environ.get('OSC_CONFIG', '~/.oscrc')
|
2007-08-22 10:30:53 +02:00
|
|
|
conffile = os.path.expanduser(conffile)
|
2006-10-10 16:04:34 +02:00
|
|
|
|
|
|
|
if not os.path.exists(conffile):
|
|
|
|
|
|
|
|
# okay, let's create a fresh config file
|
|
|
|
# if credentials are found in .netrc, use those
|
|
|
|
# otherwise ask
|
|
|
|
|
|
|
|
config = DEFAULTS.copy()
|
|
|
|
|
|
|
|
# try .netrc
|
|
|
|
# the needed entry needs to look like this:
|
|
|
|
# machine api.opensuse.org login your_login password your_pass
|
|
|
|
# note that it is not suited for credentials containing spaces
|
|
|
|
import netrc
|
|
|
|
try:
|
2007-05-10 16:29:04 +02:00
|
|
|
# XXX: apisrv is a URL now, thus requiring the "scheme" setting if https is to be used
|
2007-04-25 12:24:51 +02:00
|
|
|
netrc_host = parse_apisrv_url(None, DEFAULTS['apisrv'])[1]
|
2006-10-10 16:04:34 +02:00
|
|
|
config['user'], account, config['pass'] = \
|
2007-04-25 12:24:51 +02:00
|
|
|
netrc.netrc().authenticators(netrc_host)
|
2006-10-10 16:04:34 +02:00
|
|
|
print >>sys.stderr, 'Read credentials from %s.' % os.path.expanduser('~/.netrc')
|
|
|
|
except (IOError, TypeError, netrc.NetrcParseError):
|
|
|
|
#
|
|
|
|
# last resort... ask the user
|
|
|
|
#
|
|
|
|
import getpass
|
|
|
|
print >>sys.stderr, account_not_configured_text % conffile
|
|
|
|
config['user'] = raw_input('Username: ')
|
|
|
|
config['pass'] = getpass.getpass()
|
|
|
|
|
2007-01-12 04:53:44 +01:00
|
|
|
print >>sys.stderr, 'Creating osc configuration file %s ...' % conffile
|
2006-10-10 16:04:34 +02:00
|
|
|
fd = open(conffile, 'w')
|
|
|
|
os.chmod(conffile, 0600)
|
|
|
|
fd.write(new_conf_template % config)
|
|
|
|
fd.close()
|
2007-01-12 04:53:44 +01:00
|
|
|
print >>sys.stderr, 'done.'
|
2006-10-10 16:04:34 +02:00
|
|
|
#print >>sys.stderr, ('Now re-run the command.')
|
|
|
|
#sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
# okay, we made sure that .oscrc exists
|
|
|
|
|
2008-03-15 23:51:37 +01:00
|
|
|
cp = get_configParser(conffile)
|
2006-10-10 16:04:34 +02:00
|
|
|
|
|
|
|
if not cp.has_section('general'):
|
|
|
|
# FIXME: it might be sufficient to just assume defaults?
|
|
|
|
print >>sys.stderr, config_incomplete_text % conffile
|
|
|
|
print >>sys.stderr, new_conf_template % DEFAULTS
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
config = dict(cp.items('general', raw=1))
|
|
|
|
|
2007-04-25 12:24:51 +02:00
|
|
|
config['scheme'], config['apisrv'] = \
|
|
|
|
parse_apisrv_url(config['scheme'], config['apisrv'])
|
|
|
|
|
2007-04-25 01:00:12 +02:00
|
|
|
for i in boolean_opts:
|
|
|
|
try:
|
|
|
|
if int(config.get(i)):
|
|
|
|
config[i] = True
|
|
|
|
else:
|
|
|
|
config[i] = False
|
|
|
|
except:
|
|
|
|
sys.exit('option %s requires an integer value' % i)
|
|
|
|
|
2007-07-24 12:46:03 +02:00
|
|
|
packagecachedir = os.path.expanduser(config['packagecachedir'])
|
|
|
|
|
2006-10-10 16:04:34 +02:00
|
|
|
# transform 'url1, url2, url3' form into a list
|
|
|
|
if type(config['urllist']) == str:
|
|
|
|
config['urllist'] = [ i.strip() for i in config['urllist'].split(',') ]
|
|
|
|
|
2007-04-25 12:24:51 +02:00
|
|
|
# holds multiple usernames and passwords
|
|
|
|
auth_dict = { }
|
|
|
|
for url in [ x for x in cp.sections() if x != 'general' ]:
|
|
|
|
dummy, host = \
|
|
|
|
parse_apisrv_url(config['scheme'], url)
|
|
|
|
auth_dict[host] = { 'user': cp.get(url, 'user'),
|
|
|
|
'pass': cp.get(url, 'pass') }
|
|
|
|
|
2006-10-10 16:04:34 +02:00
|
|
|
# add the auth data we collected to the config dict
|
|
|
|
config['auth_dict'] = auth_dict
|
|
|
|
|
2007-08-22 12:18:25 +02:00
|
|
|
# override values which we were called with
|
|
|
|
if override_http_debug:
|
|
|
|
config['http_debug'] = override_http_debug
|
|
|
|
if override_apisrv:
|
|
|
|
config['scheme'], config['apisrv'] = \
|
|
|
|
parse_apisrv_url(config['scheme'], override_apisrv)
|
|
|
|
|
|
|
|
# to make the mess complete, set up the more convenient api url which we'll rather use
|
|
|
|
config['apiurl'] = config['scheme'] + '://' + config['apisrv']
|
|
|
|
|
|
|
|
# XXX unless config['user'] goes away (and is replaced with a handy function, or
|
2008-03-13 17:15:41 +01:00
|
|
|
# config becomes an object, even better), set the global 'user' here as well,
|
|
|
|
# provided that there _are_ credentials for the chosen apisrv:
|
|
|
|
if config['apisrv'] in config['auth_dict'].keys():
|
|
|
|
config['user'] = config['auth_dict'][config['apisrv']]['user']
|
2007-08-22 12:18:25 +02:00
|
|
|
|
|
|
|
# finally, initialize urllib2 for to use the credentials for Basic Authentication
|
|
|
|
init_basicauth(config)
|
|
|
|
|