~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/launchpad/scripts/debsync.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-08-02 23:29:39 UTC
  • mfrom: (13582.1.3 murder-poppy)
  • Revision ID: launchpad@pqm.canonical.com-20110802232939-bn6kxbaq765v5yc7
[r=julian-edwards][no-qa] Remove the old FTP-only poppy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Functions related to the import of Debbugs bugs into Malone."""
 
5
 
 
6
__all__ = [
 
7
    'bug_filter',
 
8
    'do_import',
 
9
    'import_bug',
 
10
    ]
 
11
 
 
12
__metaclass__ = type
 
13
 
 
14
import datetime
 
15
import sys
 
16
 
 
17
from zope.component import getUtility
 
18
 
 
19
from canonical.database.sqlbase import flush_database_updates
 
20
from lp.app.errors import NotFoundError
 
21
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 
22
from lp.bugs.interfaces.bug import (
 
23
    CreateBugParams,
 
24
    IBugSet,
 
25
    )
 
26
from lp.bugs.interfaces.bugwatch import IBugWatchSet
 
27
from lp.bugs.interfaces.cve import ICveSet
 
28
from lp.bugs.scripts import debbugs
 
29
from lp.services.encoding import guess as ensure_unicode
 
30
from lp.services.messages.interfaces.message import (
 
31
    IMessageSet,
 
32
    InvalidEmailMessage,
 
33
    UnknownSender,
 
34
    )
 
35
 
 
36
 
 
37
def bug_filter(bug, previous_import_set, target_bugs, target_package_set,
 
38
    minimum_age):
 
39
    """Function to choose which debian bugs will get processed by the sync
 
40
    script.
 
41
    """
 
42
    # don't re-import one that exists already
 
43
    if str(bug.id) in previous_import_set:
 
44
        return False
 
45
    # if we've been given a list, import only those
 
46
    if target_bugs:
 
47
        if bug.id in target_bugs:
 
48
            return True
 
49
        return False
 
50
    # we only want bugs in Sid
 
51
    if not bug.affects_unstable():
 
52
        return False
 
53
    # and we only want RC bugs
 
54
    #if not bug.is_release_critical():
 
55
    #    return False
 
56
    # and we only want bugs that affect the packages we care about:
 
57
    if not bug.affects_package(target_package_set):
 
58
        return False
 
59
    # we will not import any dup bugs (any reason to?)
 
60
    if len(bug.mergedwith) > 0:
 
61
        return False
 
62
    # and we won't import any bug that is newer than one week, to give
 
63
    # debian some time to find dups
 
64
    if bug.date > datetime.datetime.now() - datetime.timedelta(minimum_age):
 
65
        return False
 
66
    return True
 
67
 
 
68
 
 
69
def do_import(logger, max_imports, debbugs_location, target_bugs,
 
70
    target_package_set, previous_import_set, minimum_age, debbugs_pl):
 
71
 
 
72
    # figure out which bugs have been imported previously
 
73
    debbugs_tracker = getUtility(ILaunchpadCelebrities).debbugs
 
74
    for w in debbugs_tracker.watches:
 
75
        previous_import_set.add(w.remotebug)
 
76
    logger.info('%d debian bugs previously imported.' %
 
77
        len(previous_import_set))
 
78
 
 
79
    # find the new bugs to import
 
80
    logger.info('Selecting new debian bugs...')
 
81
    debbugs_db = debbugs.Database(debbugs_location, debbugs_pl)
 
82
    debian_bugs = []
 
83
    for debian_bug in debbugs_db:
 
84
        if bug_filter(debian_bug, previous_import_set, target_bugs,
 
85
            target_package_set, minimum_age):
 
86
            debian_bugs.append(debian_bug)
 
87
    logger.info('%d debian bugs ready to import.' % len(debian_bugs))
 
88
 
 
89
    # put them in ascending order
 
90
    logger.info('Sorting bugs...')
 
91
    debian_bugs.sort(lambda a, b: cmp(a.id, b.id))
 
92
 
 
93
    logger.info('Importing bugs...')
 
94
    newbugs = 0
 
95
    for debian_bug in debian_bugs:
 
96
        newbug = import_bug(debian_bug, logger)
 
97
        if newbug is True:
 
98
            newbugs += 1
 
99
        if max_imports:
 
100
            if newbugs >= max_imports:
 
101
                logger.info('Imported %d new bugs!' % newbugs)
 
102
                break
 
103
 
 
104
 
 
105
def import_bug(debian_bug, logger):
 
106
    """Consider importing a debian bug, return True if you did."""
 
107
    bugset = getUtility(IBugSet)
 
108
    debbugs_tracker = getUtility(ILaunchpadCelebrities).debbugs
 
109
    malone_bug = bugset.queryByRemoteBug(debbugs_tracker, debian_bug.id)
 
110
    if malone_bug is not None:
 
111
        logger.error('Debbugs #%d was previously imported.' % debian_bug.id)
 
112
        return False
 
113
    # get the email which started it all
 
114
    try:
 
115
        email_txt = debian_bug.comments[0]
 
116
    except IndexError:
 
117
        logger.error('No initial mail for debian #%d' % debian_bug.id)
 
118
        return False
 
119
    except debbugs.LogParseFailed, e:
 
120
        logger.warning(e)
 
121
        return False
 
122
    msg = None
 
123
    messageset = getUtility(IMessageSet)
 
124
    debian = getUtility(ILaunchpadCelebrities).debian
 
125
    ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
 
126
    try:
 
127
        msg = messageset.fromEmail(email_txt, distribution=debian,
 
128
            create_missing_persons=True)
 
129
    except UnknownSender:
 
130
        logger.error('Cannot create person for %s' % sys.exc_value)
 
131
    except InvalidEmailMessage:
 
132
        logger.error('Invalid email: %s' % sys.exc_value)
 
133
    if msg is None:
 
134
        logger.error('Failed to import debian #%d' % debian_bug.id)
 
135
        return False
 
136
 
 
137
    # get the bug details
 
138
    title = debian_bug.subject
 
139
    if not title:
 
140
        title = 'Debbugs #%d with no title' % debian_bug.id
 
141
    title = ensure_unicode(title)
 
142
    # debian_bug.package may have ,-separated package names, but
 
143
    # debian_bug.packagelist[0] is going to be a single package name for
 
144
    # sure. we work through the package list, try to find one we can
 
145
    # work with, otherwise give up
 
146
    srcpkg = pkgname = None
 
147
    for pkgname in debian_bug.packagelist():
 
148
        try:
 
149
            srcpkg = ubuntu.guessPublishedSourcePackageName(pkgname)
 
150
        except NotFoundError:
 
151
            logger.error(sys.exc_value)
 
152
    if srcpkg is None:
 
153
        # none of the package names gave us a source package we can use
 
154
        # XXX sabdfl 2005-09-16: Maybe this should just be connected to the
 
155
        # distro, and allowed to wait for re-assignment to a specific package?
 
156
        logger.error('Unable to find package details for %s' % (
 
157
            debian_bug.package))
 
158
        return False
 
159
    # sometimes debbugs has initial emails that contain the package name, we
 
160
    # can remove that
 
161
    if title.startswith(pkgname + ':'):
 
162
        title = title[len(pkgname) + 2:].strip()
 
163
    params = CreateBugParams(
 
164
        title=title, msg=msg, owner=msg.owner,
 
165
        datecreated=msg.datecreated)
 
166
    params.setBugTarget(distribution=debian, sourcepackagename=srcpkg)
 
167
    malone_bug = bugset.createBug(params)
 
168
    # create a debwatch for this bug
 
169
    thewatch = malone_bug.addWatch(debbugs_tracker, str(debian_bug.id),
 
170
        malone_bug.owner)
 
171
    thewatch.remotestatus = debian_bug.status
 
172
 
 
173
    # link the relevant task to this watch
 
174
    assert len(malone_bug.bugtasks) == 1, 'New bug should have only one task'
 
175
    task = malone_bug.bugtasks[0]
 
176
    task.bugwatch = thewatch
 
177
    task.setStatusFromDebbugs(debian_bug.status)
 
178
    task.setSeverityFromDebbugs(debian_bug.severity)
 
179
 
 
180
    # Let the world know about it!
 
181
    logger.info('%d/%s: %s: %s' % (
 
182
        debian_bug.id, malone_bug.id, debian_bug.package, title))
 
183
 
 
184
    # now we need to analyse the message for bugwatch clues
 
185
    bugwatchset = getUtility(IBugWatchSet)
 
186
    watches = bugwatchset.fromMessage(msg, malone_bug)
 
187
    for watch in watches:
 
188
        logger.info('New watch for %s on %s' % (watch.bug.id, watch.url))
 
189
 
 
190
    # and also for CVE ref clues
 
191
    cveset = getUtility(ICveSet)
 
192
    cves = cveset.inMessage(msg)
 
193
    prior_cves = malone_bug.cves
 
194
    for cve in cves:
 
195
        if cve not in prior_cves:
 
196
            malone_bug.linkCVE(cve)
 
197
            logger.info('CVE-%s (%s) found for Malone #%s' % (
 
198
                cve.sequence, cve.status.name, malone_bug.id))
 
199
 
 
200
    flush_database_updates()
 
201
    return True