Merge lp:~lifeless/python-oops-tools/prune into lp:python-oops-tools

Proposed by Robert Collins
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 23
Proposed branch: lp:~lifeless/python-oops-tools/prune
Merge into: lp:python-oops-tools
Diff against target: 250 lines (+188/-1)
7 files modified
.bzrignore (+1/-0)
setup.py (+1/-0)
src/oopstools/NEWS.txt (+3/-0)
src/oopstools/oops/migrations/0020_add_oops_prune.py (+40/-0)
src/oopstools/oops/models.py (+19/-0)
src/oopstools/scripts/prune.py (+123/-0)
versions.cfg (+1/-1)
To merge this branch: bzr merge lp:~lifeless/python-oops-tools/prune
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+82840@code.launchpad.net

Commit message

Permit pruning of OOPS from the DB that are not referenced by a bug report after a week.

Description of the change

This implements oops pruning building on the pruning present in datedir-repo. The custom code for python-oops-tools, that isn't part of the CLI, is uhm trivial. I've got room to add tests, but they seemed of vanishing utility here so I'm going to throw myself on the mercy of my reviewer.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

+ oldest_oops = Oops.objects.order_by('id')[0]

id isn't used as a criterion anywhere else. Perhaps sort by date?

review: Approve (code)
Revision history for this message
Robert Collins (lifeless) wrote :

we want the oldest oops in the system - there has been no other cause for that particular query to date.

lp:~lifeless/python-oops-tools/prune updated
21. By Robert Collins

Print out the path to the OOPS when amqp2disk fails to import it.

22. By Robert Collins

Permit wider prefix and appinstance fields.

23. By Robert Collins

Permit pruning of OOPS from the DB that are not referenced by a bug report after a week.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2011-10-13 20:18:51 +0000
+++ .bzrignore 2011-11-21 04:15:28 +0000
@@ -14,3 +14,4 @@
14apache/*14apache/*
15src/oopstools/settings.py15src/oopstools/settings.py
16./dist16./dist
17prune.log
1718
=== modified file 'setup.py'
--- setup.py 2011-10-16 22:35:01 +0000
+++ setup.py 2011-11-21 04:15:28 +0000
@@ -83,6 +83,7 @@
83 'amqp2disk = oopstools.scripts.amqp2disk:main',83 'amqp2disk = oopstools.scripts.amqp2disk:main',
84 'analyse_error_reports = oopstools.scripts.analyse_error_reports:main',84 'analyse_error_reports = oopstools.scripts.analyse_error_reports:main',
85 'load_sample_data = oopstools.scripts.load_sample_data:main',85 'load_sample_data = oopstools.scripts.load_sample_data:main',
86 'prune = oopstools.scripts.prune:main',
86 'update_db = oopstools.scripts.update_db:main',87 'update_db = oopstools.scripts.update_db:main',
87 'dir_finder = oopstools.scripts.dir_finder:main',88 'dir_finder = oopstools.scripts.dir_finder:main',
88 'report = oopstools.scripts.report:main',89 'report = oopstools.scripts.report:main',
8990
=== modified file 'src/oopstools/NEWS.txt'
--- src/oopstools/NEWS.txt 2011-11-16 23:21:38 +0000
+++ src/oopstools/NEWS.txt 2011-11-21 04:15:28 +0000
@@ -21,6 +21,9 @@
21* Mixed case OOPS reports can now be looked up in the web UI without their21* Mixed case OOPS reports can now be looked up in the web UI without their
22 OOPS-prefix (if they had one). (Robert Collins, #884571)22 OOPS-prefix (if they had one). (Robert Collins, #884571)
2323
24* Old OOPS reports can be cleaned out using the new script bin/prune.
25 (Robert Collins)
26
24* OOPS reports that don't meet the normal rules for req_vars are handled27* OOPS reports that don't meet the normal rules for req_vars are handled
25 a bit better.28 a bit better.
26 (Robert Collins, William Grant, Roman Yepishev, #885416, #884265)29 (Robert Collins, William Grant, Roman Yepishev, #885416, #884265)
2730
=== added file 'src/oopstools/oops/migrations/0020_add_oops_prune.py'
--- src/oopstools/oops/migrations/0020_add_oops_prune.py 1970-01-01 00:00:00 +0000
+++ src/oopstools/oops/migrations/0020_add_oops_prune.py 2011-11-21 04:15:28 +0000
@@ -0,0 +1,40 @@
1# Copyright 2011 Canonical Ltd. All rights reserved.
2#
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU Affero General Public License as published by
5# the Free Software Foundation, either version 3 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU Affero General Public License for more details.
12#
13# You should have received a copy of the GNU Affero General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16from south.v2 import DataMigration
17from south.db import db
18
19
20class Migration(DataMigration):
21
22 def forwards(self, orm):
23 db.create_table('oops_pruneinfo', (
24 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
25 ('pruned_until', self.gf('django.db.models.fields.DateTimeField')())
26 ))
27 db.send_create_signal('oops', ['PruneInfo'])
28
29 def backwards(self, orm):
30 db.delete_table('oops_pruneinfo')
31
32 models = {
33 'oops.prune': {
34 'Meta': {'object_name': 'PruneInfo'},
35 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
36 'pruned_until': ('django.db.models.fields.DateTimeField', [], {}),
37 },
38 }
39
40 complete_apps = ['oops']
041
=== modified file 'src/oopstools/oops/models.py'
--- src/oopstools/oops/models.py 2011-11-17 16:11:12 +0000
+++ src/oopstools/oops/models.py 2011-11-21 04:15:28 +0000
@@ -120,6 +120,25 @@
120 return unicode(self.value)120 return unicode(self.value)
121121
122122
123class PruneInfo(models.Model):
124 """Information about oops pruning."""
125
126 pruned_until = models.DateTimeField()
127
128 @classmethod
129 def prune_unreferenced(klass, prune_from, prune_until, references):
130 # XXX: This trusts date more than we may want to - should consider
131 # having a date_received separate to the date generated.
132 to_delete = set(Oops.objects.filter(
133 date__gte=prune_from, date__lte=prune_until).exclude(
134 oopsid__in=references))
135 # deleting 1 at a time is a lot of commits, but leaves the DB lively
136 # for other transactions. May need to batch this in future if its
137 # too slow.
138 for oopsid in to_delete:
139 Oops.objects.filter(oopsid__exact=oopsid).delete()
140
141
123class Report(models.Model):142class Report(models.Model):
124 """A summary report for OOPSes of a set of prefixes."""143 """A summary report for OOPSes of a set of prefixes."""
125144
126145
=== added file 'src/oopstools/scripts/prune.py'
--- src/oopstools/scripts/prune.py 1970-01-01 00:00:00 +0000
+++ src/oopstools/scripts/prune.py 2011-11-21 04:15:28 +0000
@@ -0,0 +1,123 @@
1# Copyright 2011 Canonical Ltd. All rights reserved.
2#
3# This program is free software: you can redistribute it and/or modify
4# it under the terms of the GNU Affero General Public License as published by
5# the Free Software Foundation, either version 3 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU Affero General Public License for more details.
12#
13# You should have received a copy of the GNU Affero General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16# Delete DB records of OOPSes that have no bug reports for them.
17
18__metaclass__ = type
19
20import datetime
21import logging
22import optparse
23import sys
24from textwrap import dedent
25
26from oops_datedir_repo.prune import LaunchpadTracker
27from pytz import utc
28
29from oopstools.oops.models import (
30 Oops,
31 PruneInfo,
32 )
33
34
35def main(argv=None, tracker=LaunchpadTracker, logging=logging):
36 """Console script entry point."""
37 if argv is None:
38 argv = sys.argv
39 usage = dedent("""\
40 %prog [options]
41
42 The following options must be supplied:
43 Either
44 --project
45 or
46 --projectgroup
47
48 e.g.
49 %prog --projectgroup launchpad-project
50
51 Will process every member project of launchpad-project.
52
53 When run this program will ask Launchpad for OOPS references made since
54 the last date it pruned up to, with an upper limit of one week from
55 today. It then looks in the database for all oopses created during that
56 date range, and if they are not in the set returned by Launchpad,
57 deletes them. If the database has never been pruned before, it will
58 pick the earliest date present in the repository as the start date.
59
60 This does not delete OOPS files that are on disk (e.g. created by
61 amqp2disk) - a separate pruner is available in oops-datedir-repo that
62 should be used to prune them.
63 """)
64 description = \
65 "Delete OOPS reports that are not referenced in a bug tracker."
66 parser = optparse.OptionParser(
67 description=description, usage=usage)
68 parser.add_option('--project',
69 help="Launchpad project to find references in.")
70 parser.add_option('--projectgroup',
71 help="Launchpad project group to find references in.")
72 parser.add_option(
73 '--lpinstance', help="Launchpad instance to use", default="production")
74 options, args = parser.parse_args(argv[1:])
75 def needed(*optnames):
76 present = set()
77 for optname in optnames:
78 if getattr(options, optname, None) is not None:
79 present.add(optname)
80 if not present:
81 if len(optnames) == 1:
82 raise ValueError('Option "%s" must be supplied' % optname)
83 else:
84 raise ValueError(
85 'One of options %s must be supplied' % (optnames,))
86 elif len(present) != 1:
87 raise ValueError(
88 'Only one of options %s can be supplied' % (optnames,))
89 needed('project', 'projectgroup')
90 logging.basicConfig(
91 filename='prune.log', filemode='w', level=logging.DEBUG)
92 one_week = datetime.timedelta(weeks=1)
93 one_day = datetime.timedelta(days=1)
94 # Only prune OOPS reports more than one week old.
95 prune_until = datetime.datetime.now(utc) - one_week
96 # Ignore OOPS reports we already found references for - older than the last
97 # prune date.
98 try:
99 info = PruneInfo.objects.all()[0]
100 except IndexError:
101 # Never been pruned.
102 try:
103 oldest_oops = Oops.objects.order_by('id')[0]
104 except IndexError:
105 # And has no oopses
106 return 0
107 info = PruneInfo(pruned_until=oldest_oops.date-one_day)
108 info.save()
109 prune_from = info.pruned_until
110 if prune_from.tzinfo is None:
111 # Workaround django tz handling bug:
112 # https://code.djangoproject.com/ticket/17062
113 prune_from = prune_from.replace(tzinfo=utc)
114 # The tracker finds all the references for the selected dates.
115 finder = tracker(options)
116 references = finder.find_oops_references(
117 prune_from, prune_until, options.project, options.projectgroup)
118 # Then we can delete the unreferenced oopses.
119 PruneInfo.prune_unreferenced(prune_from, prune_until, references)
120 # And finally save the fact we have scanned up to the selected date.
121 info.pruned_until = prune_until
122 info.save()
123 return 0
0124
=== modified file 'versions.cfg'
--- versions.cfg 2011-11-16 07:25:56 +0000
+++ versions.cfg 2011-11-21 04:15:28 +0000
@@ -21,7 +21,7 @@
21mechanize = 0.1.1121mechanize = 0.1.11
22oops = 0.0.1022oops = 0.0.10
23oops-amqp = 0.0.423oops-amqp = 0.0.4
24oops-datedir-repo = 0.0.1224oops-datedir-repo = 0.0.14
25setuptools = 0.6c1125setuptools = 0.6c11
26z3c.recipe.filetemplate = 2.0.326z3c.recipe.filetemplate = 2.0.3
27z3c.recipe.sphinxdoc = 0.0.827z3c.recipe.sphinxdoc = 0.0.8

Subscribers

People subscribed via source and target branches

to all changes: