~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#!/usr/bin/python -S
8687.15.7 by Karl Fogel
Add the copyright header block to more files.
2
#
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
5
6
# This script runs through the set of Debbugs watches, and tries to
7
# syncronise each of those to the malone bug which is watching it.
8
9641.1.4 by Gary Poster
more updates for _pythonpath. Still need to update all subprocess calls to python to use -S; still need to update z3c.recipe.filetemplate to provide sys.modules from -S run.
9
import _pythonpath
2450 by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish
10
import os
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
11
import sys
12
import email
13
import logging
14
15
# zope bits
16
from zope.component import getUtility
17
8523.3.1 by Gavin Panella
Bugs tree reorg after automated migration.
18
from lp.bugs.scripts import debbugs
8356.1.1 by Leonard Richardson
Partial move.
19
from lp.services.scripts.base import (LaunchpadCronScript,
3691.348.19 by kiko
Two more scripts converted.
20
    LaunchpadScriptFailure)
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
21
from canonical.launchpad.interfaces import (IBugSet,
3691.348.1 by kiko
Remove the original lockfile class and use our contributed GlobalLock everywhere to avoid stale locks making our scripts stop to run. Update a bunch of scripts to use it. Hopefully backwards-compatible enough to survive tests.
22
    ILaunchpadCelebrities, InvalidEmailMessage, IBugTaskSet,
3691.348.19 by kiko
Two more scripts converted.
23
    IBugWatchSet, IMessageSet, ICveSet, BugTaskSearchParams)
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
24
from canonical.database.constants import UTC_NOW
25
26
# setup core values and defaults
2450 by Canonical.com Patch Queue Manager
[r=jamesh] rework cve structure, and general polish
27
debbugs_location_default = '/srv/bugs-mirror.debian.org/'
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
28
3691.348.19 by kiko
Two more scripts converted.
29
4264.2.1 by James Henstridge
add a LaunchpadCronScript subclass, and make cronscripts/*.py use it
30
class DebWatchUpdater(LaunchpadCronScript):
3691.348.19 by kiko
Two more scripts converted.
31
    loglevel = logging.WARNING
32
33
    def add_my_options(self):
34
        self.parser.add_option('--max', action='store', type='int', dest='max',
35
            default=None, help="The maximum number of bugs to synchronise.")
36
        self.parser.add_option('--debbugs', action='store', type='string',
37
            dest='debbugs',
38
            default=debbugs_location_default,
39
            help="The location of your debbugs database.")
40
41
    def main(self):
42
        if not os.path.exists(os.path.join(self.options.debbugs, 'index/index.db')):
43
            raise LaunchpadScriptFailure('%s is not a debbugs db.'
44
                                         % self.options.debbugs)
45
46
        self.txn.begin()
47
        self.debbugs_db = debbugs.Database(self.options.debbugs)
48
        self.sync()
49
        self.txn.commit()
50
51
        self.logger.info('Done!')
52
53
    def sync(self):
54
        changedcounter = 0
55
56
        self.logger.info('Finding existing debbugs watches...')
57
        debbugs_tracker = getUtility(ILaunchpadCelebrities).debbugs
58
        debwatches = debbugs_tracker.watches
59
60
        previousimportset = set([b.remotebug for b in debwatches])
61
        self.logger.info('%d debbugs previously imported.' % len(previousimportset))
62
63
        target_watches = [watch for watch in debwatches if watch.needscheck]
64
        self.logger.info('%d debbugs watches to syncronise.' % len(target_watches))
65
66
        self.logger.info('Sorting bug watches...')
67
        target_watches.sort(key=lambda a: a.remotebug)
68
69
        self.logger.info('Syncing bug watches...')
70
        for watch in target_watches:
71
            if self.sync_watch(watch):
72
                changedcounter += 1
73
                self.txn.commit()
74
            if self.options.max:
75
                if changedcounter >= self.options.max:
76
                    self.logger.info('Synchronised %d bugs!' % changedcounter)
77
                    return
78
79
    def sync_watch(self, watch):
80
        # keep track of whether or not something changed
81
        waschanged = False
82
        # find the bug in malone
83
        malone_bug = watch.bug
84
        # find the bug in debbugs
85
        debian_bug = self.debbugs_db[int(watch.remotebug)]
86
        bugset = getUtility(IBugSet)
87
        bugtaskset = getUtility(IBugTaskSet)
88
        bugwatchset = getUtility(IBugWatchSet)
89
        messageset = getUtility(IMessageSet)
90
        debian = getUtility(ILaunchpadCelebrities).debian
91
        debbugs_tracker = getUtility(ILaunchpadCelebrities).debbugs
92
93
        # make sure we have tasks for all the debian package linkages, and also
94
        # make sure we have updated their status and severity appropriately
95
        for packagename in debian_bug.packagelist():
96
            try:
97
                srcpkgname, binpkgname = debian.guessPackageNames(packagename)
98
            except ValueError:
99
                self.logger.error(sys.exc_value)
100
                continue
101
            search_params = BugTaskSearchParams(user=None, bug=malone_bug,
102
                sourcepackagename=srcpkgname)
103
            search_params.setDistribution(debian)
104
            bugtasks = bugtaskset.search(search_params)
105
            if len(bugtasks) == 0:
106
                # we need a new task to link the bug to the debian package
107
                self.logger.info('Linking %d and debian %s/%s' % (
108
                    malone_bug.id, srcpkgname.name, binpkgname.name))
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
109
                # XXX: kiko 2007-02-03:
110
                # This code is completely untested and broken.
3691.348.19 by kiko
Two more scripts converted.
111
                bugtask = malone_bug.addTask(
112
                    owner=malone_bug.owner, distribution=debian,
113
                    sourcepackagename=srcpkgname)
114
                bugtask.bugwatch = watch
115
                waschanged = True
116
            else:
117
                assert len(bugtasks) == 1, 'Should only find a single task'
118
                bugtask = bugtasks[0]
119
            status = bugtask.status
120
            if status <> bugtask.setStatusFromDebbugs(debian_bug.status):
121
                waschanged = True
122
            severity = bugtask.severity
123
            if severity <> bugtask.setSeverityFromDebbugs(debian_bug.severity):
124
                waschanged = True
125
126
        known_msg_ids = set([msg.rfc822msgid for msg in malone_bug.messages])
127
128
        for raw_msg in debian_bug.comments:
129
130
            # parse it so we can extract the message id easily
131
            message = email.message_from_string(raw_msg)
132
133
            # see if we already have imported a message with this id for this
134
            # bug
135
            message_id = message['message-id']
136
            if message_id in known_msg_ids:
137
                # Skipping msg that is already imported
138
                continue
139
140
            # make sure this message is in the db
141
            msg = None
142
            try:
143
                msg = messageset.fromEmail(raw_msg, parsed_message=message,
144
                    create_missing_persons=True)
145
            except InvalidEmailMessage:
146
                self.logger.error('Invalid email: %s' % sys.exc_value)
147
            if msg is None:
148
                continue
149
150
            # create the link between the bug and this message
151
            bugmsg = malone_bug.linkMessage(msg)
152
153
            # ok, this is a new message for this bug, so in effect something has
154
            # changed
155
            waschanged = True
156
157
            # now we need to analyse the message for useful data
158
            watches = bugwatchset.fromMessage(msg, malone_bug)
159
            for watch in watches:
160
                self.logger.info('New watch for #%s on %s' % (watch.bug.id, watch.url))
161
                waschanged = True
162
163
            # and also for CVE ref clues
164
            prior_cves = set(malone_bug.cves)
165
            cveset = getUtility(ICveSet)
166
            cves = cveset.inMessage(msg)
167
            for cve in cves:
168
                malone_bug.linkCVE(cve)
169
                if cve not in prior_cves:
170
                    self.logger.info('CVE-%s (%s) found for Malone #%s' % (
171
                        cve.sequence, cve.status.name, malone_bug.id))
172
173
            # now we know about this message for this bug
174
            known_msg_ids.add(message_id)
175
176
            # and best we commit, so that we can see the email that the
177
            # librarian has created in the db
178
            self.txn.commit()
179
180
        # Mark all merged bugs as duplicates of the lowest-numbered bug
181
        if (len(debian_bug.mergedwith) > 0 and
182
            min(debian_bug.mergedwith) > debian_bug.id):
183
            for merged_id in debian_bug.mergedwith:
184
                merged_bug = bugset.queryByRemoteBug(debbugs_tracker, merged_id)
185
                if merged_bug is not None:
186
                    # Bug has been imported already
187
                    if merged_bug.duplicateof == malone_bug:
188
                        # we already know about this
189
                        continue
190
                    elif merged_bug.duplicateof is not None:
191
                        # Interesting, we think it's a dup of something else
192
                        self.logger.warning('Debbugs thinks #%d is a dup of #%d' % (
193
                            merged_bug.id, merged_bug.duplicateof))
194
                        continue
195
                    # Go ahead and merge it
196
                    self.logger.info("Malone #%d is a duplicate of Malone #%d" % (
197
                        merged_bug.id, malone_bug.id))
198
                    merged_bug.duplicateof = malone_bug.id
199
200
                    # the dup status has changed
201
                    waschanged = True
202
203
        # make a note of the remote watch status, if it has changed
204
        if watch.remotestatus <> debian_bug.status:
205
            watch.remotestatus = debian_bug.status
206
            waschanged = True
207
208
        # update the watch date details
209
        watch.lastchecked = UTC_NOW
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
210
        if waschanged:
3691.348.19 by kiko
Two more scripts converted.
211
            watch.lastchanged = UTC_NOW
212
            self.logger.info('Watch on Malone #%d changed.' % watch.bug.id)
213
        return waschanged
214
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
215
216
if __name__ == '__main__':
3691.348.19 by kiko
Two more scripts converted.
217
    script = DebWatchUpdater('launchpad-debbugs-sync')
218
    script.lock_and_run()
2048 by Canonical.com Patch Queue Manager
debbugssync, hct enabling, and ui fixes. r=jamesh
219