home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/python
-
- # Download a Launchpad bug report, get its source package, check if it has
- # apport hooks, and if so, run and upload them.
- #
- # Copyright (c) 2009 Canonical Ltd.
- # Author: Martin Pitt <martin.pitt@ubuntu.com>
- #
- # This program is free software; you can redistribute it and/or modify it
- # under the terms of the GNU General Public License as published by the
- # Free Software Foundation; either version 2 of the License, or (at your
- # option) any later version. See http://www.gnu.org/copyleft/gpl.html for
- # the full text of the license.
-
- import sys, os.path, optparse, tempfile, atexit, shutil, re, email
- from glob import glob
-
- import apport
-
- bug_target_re = re.compile(
- r'/ubuntu/(?:(?P<suite>[^/]+)/)?\+source/(?P<source>[^/]+)$')
-
- try:
- import launchpadlib.errors
- from launchpadlib.launchpad import Launchpad, STAGING_SERVICE_ROOT, EDGE_SERVICE_ROOT
- from launchpadlib.credentials import Credentials
- except ImportError:
- print >> sys.stderr, 'Please install the package "python-launchpadlib".'
- sys.exit(1)
-
- def login(launchpad_instance=EDGE_SERVICE_ROOT):
- '''Log into Launchpad.
-
- This reads/saves credentials, and returns a Launchpad instance.
- '''
- cache_dir = tempfile.mkdtemp()
- atexit.register(shutil.rmtree, cache_dir)
-
- cred_dir = os.path.expanduser('~/.cache/apport')
- if not os.path.isdir(cred_dir):
- os.makedirs(cred_dir)
- cred = os.path.join(cred_dir, 'launchpad.credentials')
-
- if os.path.exists(cred):
- # use existing credentials
- credentials = Credentials()
- credentials.load(open(cred))
- launchpad = Launchpad(credentials, launchpad_instance, cache_dir)
- else:
- # get credentials and save them
- try:
- launchpad = Launchpad.get_token_and_login('apport-collect',
- launchpad_instance, cache_dir)
- except launchpadlib.errors.HTTPError, e:
- print >> sys.stderr, 'Error connecting to Launchpad: %s\nYou have to allow "Change anything" privileges.' % str(e)
- sys.exit(1)
- f = open(cred, 'w')
- os.chmod(cred, 0600)
- launchpad.credentials.save(f)
- f.close()
-
- return launchpad
-
- def upload(report, bug):
- '''Upload collected information to Launchpad bug.'''
-
- print 'Uploading additional information to Launchpad bug...'
-
- # we want to reuse the knowledge of write_mime() with all its different input
- # types and output formatting; however, we have to dissect the mime ourselves,
- # since we can't just upload it as a blob
- mime = tempfile.TemporaryFile()
- report.write_mime(mime)
- mime.flush()
- mime.seek(0)
- msg = email.message_from_file(mime)
- msg_iter = msg.walk()
-
- # first part is the multipart container
- part = msg_iter.next()
- assert part.is_multipart()
-
- # second part should be an inline text/plain attachments with all short
- # fields
- part = msg_iter.next()
- assert not part.is_multipart()
- assert part.get_content_type() == 'text/plain'
-
- print' short text data...'
- bug.newMessage(content=part.get_payload(decode=True),
- subject='apport-collect data')
-
- # other parts are the attachments:
- for part in msg_iter:
- print ' attachment: %s...' % part.get_filename()
- bug.addAttachment(comment='', data=part.get_payload(decode=True),
- filename=part.get_filename(), is_patch=False)
-
- def collect(report, package):
- '''Collect information for given package.'''
-
- print 'Collecting apport information for source package %s...' % package
- try:
- report.add_package_info(package)
- except ValueError:
- # this happens for source package tasks which do not have an identical
- # binary package name
- pass
- report.add_hooks_info()
-
- #
- # main
- #
-
- optparser = optparse.OptionParser('%prog [options] <Launchpad bug number>')
- optparser.add_option('-p', '--package',
- help="Collect information for this package. If not given, it will be inferred from the bug report's source package tasks.",
- type='string', dest='package')
- (opts, args) = optparser.parse_args()
-
- if len(args) != 1:
- optparser.error('incorrect number of arguments; use --help for a short online help')
- sys.exit(1)
-
- (bug_number,) = args
-
- print 'Logging into Launchpad...'
- if os.getenv('APPORT_STAGING'):
- lp = login(STAGING_SERVICE_ROOT)
- else:
- lp = login()
-
- print 'Downloading bug...'
- bug = lp.bugs[int(bug_number)]
-
- report = apport.Report('Bug')
-
- print 'Bug title:', bug.title
- if opts.package:
- collect(report, opts.package)
- else:
- # determine bug tasks and collect for those
- for task in bug.bug_tasks:
- match = bug_target_re.search(task.target.self_link)
- if not match:
- print 'Ignoring task', task.target
- continue
- if task.status in ('Invalid', "Won't Fix", 'Fix Released'):
- print 'Ignoring task %s because it is closed' % task.target
- continue
- src = match.group('source')
- report['SourcePackage'] = src
- report['Package'] = src # no way to find this out
- collect(report, src)
-
- report.add_os_info()
- report.add_user_info()
- report.add_proc_environ()
-
- # delete the uninteresting keys
- del report['ProblemType']
- del report['Date']
- try:
- del report['SourcePackage']
- except KeyError:
- pass
-
- if len(report.keys()) == 0:
- print 'No additional information collected.'
- sys.exit(0)
-
- try:
- upload(report, bug)
- except launchpadlib.errors.HTTPError, e:
- print >> sys.stderr, 'Error connecting to Launchpad: %s\nYou have to allow "Change anything" privileges.' % str(e)
- sys.exit(1)
-