Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 7c5492faef
@solj solj
executable file 545 lines (455 sloc) 18.693 kB
#!/usr/bin/python
### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU Library General Public License as published by
### the Free Software Foundation; version 2 only
###
### This program 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 Library General Public License for more details.
###
### You should have received a copy of the GNU Library General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
### Copyright 2004-2006 Dag Wieers <dag@wieers.com>
import fnmatch
import getopt
import getpass
import glob
import os
import signal
import sys
import types
import urlparse
import xmlrpclib
if os.path.exists('/usr/share/mrepo/up2date_client/'):
sys.path[:0] = ['/usr/share/mrepo/', '/usr/share/mrepo/up2date_client/']
elif os.path.exists('/usr/share/rhn/up2date_client/'):
sys.path[:0] = ['/usr/share/rhn/', '/usr/share/rhn/up2date_client/']
else:
print >> sys.stderr, 'rhnget: up2date libraries are not installed. Aborting execution'
sys.exit(1)
from up2date_client import config, rpcServer, wrapperUtils, up2dateErrors, repoDirector
from rhn import rpclib
cfg = {}
loginInfo = {}
rd = None
__version__ = "$Revision: 4786 $"
# $Source$
VERSION = '0.8.4svn'
### Register rhn and rhns as a known schemes
for scheme in ('rhn', 'rhns'):
urlparse.uses_netloc.insert(0, scheme)
urlparse.uses_query.insert(0, scheme)
class Options:
def __init__(self, args):
self.cleanup = False
self.downloadall = False
self.dryrun = False
self.filter = None
self.list = None
self.quiet = False
self.rhnpassword = None
self.rhnrelease = None
self.rhnusername = None
self.source = False
self.systemid = '/etc/sysconfig/rhn/systemid'
self.verbose = 1
try:
opts, args = getopt.getopt(args, 'hlnqp:r:s:u:v',
('delete', 'download-all', 'dry-run', 'filter=', 'help', 'list', 'password=', 'quiet',
'release=', 'source', 'systemid=', 'username=', 'verbose', 'version'))
except getopt.error, exc:
print 'rhnget: %s, try rhnget -h for a list of all the options' % str(exc)
sys.exit(1)
for opt, arg in opts:
if opt in ('--delete', ):
self.cleanup = True
elif opt in ('--download-all', ):
self.downloadall = True
elif opt in ('--filter', ):
self.filter = arg
self.downloadall = True
elif opt in ('-h', '--help'):
self.usage()
print
self.help()
sys.exit(0)
elif opt in ('-l', '--list'):
self.list = True
self.downloadall = True
elif opt in ('-n', '--dry-run'):
self.dryrun = True
elif opt in ['-p', '--password']:
self.rhnpassword = arg
elif opt in ('-q', '--quiet'):
self.quiet = True
elif opt in ('-r', '--release'):
self.rhnrelease = arg
elif opt in ('--source', ):
self.source = True
elif opt in ('-s', '--systemid'):
self.systemid = os.path.abspath(arg)
elif opt in ['-u', '--username']:
self.rhnusername = arg
elif opt in ('-v', '--verbose'):
self.verbose = self.verbose + 1
elif opt in ('--version', ):
self.version()
sys.exit(0)
if len(args) < 1:
self.usage()
print
self.help()
sys.exit(1)
self.uri = args[0]
if len(args) == 2:
self.destination = args[1]
else:
self.destination = os.getcwd()
if self.quiet:
self.verbose = 0
if self.verbose >= 3:
print 'Verbosity set to level %d' % (self.verbose - 1)
def version(self):
print 'rhnget %s' % VERSION
print 'Written by Dag Wieers <dag@wieers.com>'
print
print 'platform %s/%s' % (os.name, sys.platform)
print 'python %s' % sys.version
print
print 'build revision $Rev: 4786 $'
def usage(self):
print 'usage: rhnget [options] rhns://server/channel destination-path'
def help(self):
print '''Download packages from Red Hat Network (RHN)
rhnget options:
--delete delete files that are not on the sender side
--download-all download all package versions available
--filter filter packages based on regexp
-l, --list list the available packages
-n, --dry-run show what would have been done
-q, --quiet minimal output
-r, --release=release specify the RHN release (if different from the systemid)
--source download source packages
-s, --systemid=file systemid to use
-v, --verbose increase verbosity
-vv, -vvv, -vvvv.. increase verbosity more
'''
class Set:
def __init__(self):
self.list = []
def add(self, input):
if input not in self.list:
self.list.append(input)
def delete(self, input):
if input in self.list:
self.list.removed(input)
def difference(self, other):
newlist = Set()
for element in self.list:
if element not in other.list:
newlist.add(element)
return newlist
def sort(self):
return self.list.sort()
def __str__(self):
return '\n\t' + '\n\t'.join([element[0] for element in self.list])
def __len__(self):
return len(self.list)
class MirrorException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
def error(level, str):
"Output error message"
if level <= op.verbose:
sys.stderr.write('rhnget: %s\n' % str)
def info(level, str):
"Output info message"
if level <= op.verbose:
sys.stdout.write('%s\n' % str)
def die(ret, str):
"Print error and exit with errorcode"
error(0, str)
sys.exit(ret)
def remove(file):
"Remove files or directories"
if isinstance(file, types.StringType):
if op.dryrun:
return
if os.path.islink(file):
os.unlink(file)
elif os.path.isdir(file):
try:
os.rmdir(file)
except:
os.path.walk(file, removedir, ())
os.rmdir(file)
elif os.path.isfile(file) or os.path.islink(file):
os.unlink(file)
else:
for f in file:
remove(f)
def removedir(void, dir, files):
for file in files:
remove(os.path.join(dir, file))
def mkdir(path):
"Create a directory, and parents if needed"
if op.dryrun:
return
if os.path.islink(path):
os.unlink(path)
if not os.path.exists(path):
os.makedirs(path)
def rhnlogin(url, path, force=False):
'Log on to RHN and return cfg, loginInfo and systemid'
global cfg, loginInfo, rd, config, rpcServer
### Look for the usual suspects
if os.path.isfile(op.systemid):
systemidpath = op.systemid
elif os.path.isfile('/etc/sysconfig/rhn/systemid'):
systemidpath = '/etc/sysconfig/rhn/systemid'
else:
info(1, 'No RHN systemid found, skipping download.')
return
info(3, 'Using RHN systemid from %s' % systemidpath)
systemid = open(systemidpath).read()
cfg['systemIdPath'] = systemidpath
cfg = config.initUp2dateConfig()
cfg['systemIdPath'] = systemidpath
cfg['storageDir'] = path
cfg['retrieveOnly'] = 1
cfg['keepAfterInstall'] = 1
cfg['noReboot'] = 1
cfg['useRhn'] = 1
cfg['showChannels'] = 1
cfg['showAvailablePackages'] = 1
cfg['isatty'] = 1
cfg['networkRetries'] = 3
# cfg['headerFetchCount'] = 20
cfg['enableProxy'] = 0
cfg['enableProxyAuth'] = 0
cfg['httpProxy'] = ''
cfg['proxyUser='] = ''
cfg['proxyPassword'] = ''
cfg["sslCACert"] = '/usr/share/rhn/RHNS-CA-CERT'
### Override the version if forced in mrepo configuration (to allow single systemid usage)
if op.rhnrelease:
cfg['versionOverride'] = op.rhnrelease
else:
cfg['versionOverride'] = rpclib.xmlrpclib.loads(systemid)[0][0]['os_release']
info(3, 'Using RHN release %s' % cfg['versionOverride'])
# if op.arch:
# cfg['forceArch'] = '%s-redhat-linux' % op.arch
### Modify the logfile in case we have no rights to write in /var/log/up2date (non-root)
if os.access('/var/log/up2date', os.W_OK):
cfg['logFile'] = '/var/log/up2date'
else:
cfg['logFile'] = os.path.expanduser('~/up2date.log')
### If we're not targetting the default RHN server, change the location
rhnscheme, rhnserver, t, t, t, t = urlparse.urlparse(url)
if not rhnserver:
rhnserver = 'xmlrpc.rhn.redhat.com'
cfg['noSSLServerURL'] = 'http://%s/XMLRPC' % rhnserver
if rhnscheme == 'rhn':
cfg['serverURL'] = 'http://%s/XMLRPC' % rhnserver
else:
cfg['serverURL'] = 'https://%s/XMLRPC' % rhnserver
### Get proxy information from environment and set up2date config accordingly
proxy = None
if os.environ.has_key('http_proxy') and rhnscheme == 'rhn':
t, proxy, t, t, t, t = urlparse.urlparse(os.environ['http_proxy'])
elif os.environ.has_key('https_proxy') and rhnscheme == 'rhns':
t, proxy, t, t, t, t = urlparse.urlparse(os.environ['https_proxy'])
if proxy:
cfg['enableProxy'] = 1
cfg['httpProxy'] = proxy
info(4, 'Setting proxy for %s to %s' % (rhnscheme, proxy))
### FIXME: Implement proxy authentication
# if proxy.username and proxy.password:
# cfg['enableProxyAuth'] = 1
# cfg['proxyPassword'] = proxy.password
# cfg['proxyUser='] = proxy.username
### Set debugging information to something very high (there seems to be no granularity)
if op.verbose >= 3:
cfg['debug'] = 1
info(4, '\nBEFORE LOGIN: logininfo: %s\n' % loginInfo)
try:
server = rpcServer.getServer()
li = rpcServer.doCall(server.up2date.login, systemid)
loginInfo.update(li)
except rpclib.Fault, f:
error(1, 'Error logging in with systemid %s. %s' % (systemidpath, f.faultString))
return None
info(4, '\nAFTER LOGIN: logininfo: %s\n' % loginInfo)
return systemid
def rhngetchannel(channels, label):
'Return the channel with given label, if found'
for c in channels:
if isinstance(c, types.ListType):
l, v = c[0], c[1]
else:
l, v = c['label'], c['version']
if l == label:
return {
'label': l,
'version': v,
'type': 'up2date',
'url': cfg['serverURL'],
}
return None
def mirrorrhn(url, path):
'Mirror a channel from RHN'
global cfg, loginInfo, rd, repoDirector, rpcServer
t, t, label, t, t, t = urlparse.urlparse(url)
label = label.strip('/')
### Log on to RHN
systemid = rhnlogin(url, path)
if not systemid:
return
mkdir(cfg['storageDir'])
### Try to find a channel with label
channel = rhngetchannel(loginInfo.get('X-RHN-Auth-Channels'), label)
if not channel:
# raise(Exception('Error system not subscribe to channel %s, skipping.' % label))
if not op.rhnusername:
op.rhnusername = raw_input('RHN Username: ')
if op.rhnusername and not op.rhnpassword:
op.rhnpassword = getpass.getpass('RHN Password for user %s: ' % op.rhnusername)
if op.rhnusername and op.rhnpassword:
try:
server = rpcServer.getServer()
channels = rpcServer.doCall(server.up2date.subscribeChannels, systemid, (label,), op.rhnusername, op.rhnpassword)
except rpclib.Fault, f:
raise(Exception('Error subscribing to channel %s, skipping.%s' % (label, f.faultString)))
systemid = rhnlogin(url, path, force=True)
if not systemid:
return
info(4, '\nAFTER SUBSC: logininfo: %s\n' % loginInfo)
channel = rhngetchannel(loginInfo.get('X-RHN-Auth-Channels'), label)
if not channel:
raise(Exception('Failed to subscribe RHN id to channel %s, skipping.' % label))
else:
raise(Exception('No RHN username or password supplied. Please add channel %s on RHN website manually. Skipping.' % label))
### Download packagelist for this channel
try:
repos = repoDirector.initRepoDirector()
except xmlrpclib.Fault, f:
raise(MirrorException('Problem setting up XML communication for channel %s.\n%s' % (label, f.faultString)))
return
except up2dateErrors.ServerCapabilityError, e:
raise(MirrorException('Problem negotiating capabilities for channel %s.\n%s' % (label, e)))
return
try:
if op.downloadall == True:
package_list, type = rpcServer.doCall(repos.listAllPackages, channel, None, None)
else:
package_list, type = rpcServer.doCall(repos.listPackages, channel, None, None)
except rpclib.Fault, f:
raise(MirrorException('Error listing packages from channel %s. Skipping. %s' % (label, f.faultString)))
except up2dateErrors.CommunicationError, e:
raise(MirrorException('Error listing packages from channel %s. Skipping.\n%s' % (label, e)))
except KeyError, e:
if e == "'up2date'":
raise(MirrorException('Missing up2date entry in /etc/sysconfig/rhn/sources.'))
else:
raise(MirrorException('Unknown error that needs more debugging occured with channel %s. Skipping.\n%s' % (label, e)))
### Download packages from the packagelist
signal.signal(signal.SIGINT, signal.SIG_DFL)
for pkg in package_list:
### FIXME: Check if not already on ISO-file or repository as well
filename = '%s-%s-%s.%s.rpm' % (pkg[0], pkg[1], pkg[2], pkg[4])
filesize = int(pkg[5])
### Filter packagelist
if op.filter and not fnmatch.fnmatch(filename, op.filter):
info(4, 'Packages %s excluded by filter' % filename)
continue
### List only files if requested
if op.list:
info(0, filename)
continue
### If file (or symlink target) exists
if os.path.isfile(os.path.join(path, filename)):
stat = os.stat(os.path.join(path, filename))
if stat.st_size == filesize:
info(3, 'File %s is already in %s' % (filename, path))
continue
else:
info(2, 'File %s has wrong size (found: %s, expected: %s), refetching.' % (filename, stat.st_size, filesize))
remove(os.path.join(path, filename))
### If symlink target does not exist, remove symlink
elif os.path.islink(os.path.join(path, filename)):
remove(os.path.join(path, filename))
if op.dryrun:
info(1, 'Not downloading package %s' % filename)
continue
try:
if op.verbose <= 1:
rpcServer.doCall(repos.getPackage, pkg, None, None)
else:
rpcServer.doCall(repos.getPackage, pkg, wrapperUtils.printPkg, wrapperUtils.printRetrieveHash)
except rpclib.Fault, f:
error(0, 'rpcError: Error getting package %s from %s. %s' % (filename, label, f.faultString))
except TypeError, e:
error(0, 'TypeError: Error downloading package %s from %s. Skipping.\n%s' % (filename, label, e))
except up2dateErrors.CommunicationError, e:
error(0, 'CommunicationError: Error downloading package %s from %s. Skipping.\n%s' % (filename, label, e))
if op.source:
try:
hdr, call_type = rpcServer.doCall(repos.getHeader, pkg)
srcrpm = hdr['sourcerpm']
if op.verbose <= 1:
rpcServer.doCall(repos.getPackageSource, channel, srcrpm, None, None)
else:
rpcServer.doCall(repos.getPackageSource, channel, srcrpm, wrapperUtils.printPkg, wrapperUtils.printRetrieveHash)
except rpclib.Fault, f:
error(0, 'rpcError: Error getting package %s from %s. %s' % (filename, label, f.faultString))
except TypeError, e:
error(0, 'TypeError: Error downloading package %s from %s. Skipping.\n%s' % (filename, label, e))
except up2dateErrors.CommunicationError, e:
error(0, 'CommunicationError: Error downloading package %s from %s. Skipping.\n%s' % (filename, label, e))
### Remove packages on the receiver side that are not on the sender side
if op.cleanup:
### Collect receiver side
receiver = Set()
for file in glob.glob(os.path.join(path, '*.rpm')):
if os.path.exists(file):
filename = os.path.basename(file)
filesize = os.stat(file).st_size
receiver.add((filename, filesize))
receiver.sort()
### Collect sender side
sender = Set()
for pkg in package_list:
filename = '%s-%s-%s.%s.rpm' % (pkg[0], pkg[1], pkg[2], pkg[4])
filesize = int(pkg[5])
sender.add((filename, filesize))
sender.sort()
### Remove difference between receiver and sender
cleanse = receiver.difference(sender)
for filename, filesize in cleanse.list:
info(3, 'Cleaning up obsolete file %s (%d kiB)' % (filename, filesize))
remove(os.path.join(path, filename))
def main():
mirrorrhn(op.uri, op.destination)
### Unbuffered sys.stdout
sys.stdout = os.fdopen(1, 'w', 0)
sys.stderr = os.fdopen(2, 'w', 0)
### Workaround for python <= 2.2.1
try:
True, False
except NameError:
True = 1
False = 0
### Main entrance
if __name__ == '__main__':
exitcode = 0
op = Options(sys.argv[1:])
try:
main()
except KeyboardInterrupt, e:
die(6, 'Exiting on user request')
sys.exit(exitcode)
# vim:ts=4:sw=4:et
Jump to Line
Something went wrong with that request. Please try again.