1
# Copyright 2011 Canonical Ltd. All rights reserved.
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.
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.
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/>.
16
# Receive OOPS reports from AMQP and publish them into the oops-tools
24
from textwrap import dedent
26
import amqplib.client_0_8 as amqp
27
from oops import Config
29
import oops_datedir_repo
31
from oopstools.oops.helpers import parsedate, load_prefixes
32
from oopstools.oops.models import (
34
parsed_oops_to_model_oops,
38
from oopstools.oops.oopsstore import OopsStore
39
from oopstools.oops import dbsummaries
40
from oopstools.oops.summaries import (
42
CheckwatchesErrorSummary,
43
CodeHostingWithRemoteSectionSummary,
54
The following options must be supplied:
63
amqp2disk --output /srv/oops-tools/amqpoopses --host "localhost:3472" \\
64
--username "guest" --password "guest" --vhost "/" --queue "oops"
66
The AMQP environment should be setup in advance with a persistent queue
67
bound to your exchange : using transient queues would allow OOPSes to
68
be lost if the amqp2disk process were to be shutdown for a non-trivial
69
duration. The --bind-to option will cause the queue to be created and
70
bound to the given exchange. This is only needed the first time as it
71
is created persistently.
73
description = "Load OOPS reports into oops-tools from AMQP."
74
parser = optparse.OptionParser(
75
description=description, usage=usage)
76
parser.add_option('--output', help="Root directory to store OOPSes in.")
77
parser.add_option('--host', help="AQMP host / host:port.")
78
parser.add_option('--username', help="AQMP username.")
79
parser.add_option('--password', help="AQMP password.")
80
parser.add_option('--vhost', help="AMQP vhost.")
81
parser.add_option('--queue', help="AMQP queue name.")
83
'--bind-to', help="AMQP exchange to bind to (only needed once).")
84
options, args = parser.parse_args(argv[1:])
86
if getattr(options, optname, None) is None:
87
raise ValueError('option "%s" must be supplied' % optname)
94
connection = amqp.Connection(host=options.host, userid=options.username,
95
password=options.password, virtual_host=options.vhost)
96
channel = connection.channel()
98
channel.queue_declare(options.queue, durable=True, auto_delete=False)
99
channel.queue_bind(options.queue, options.bind_to)
100
config = make_amqp_config(options.output)
101
receiver = oops_amqp.Receiver(config, channel, options.queue)
103
receiver.run_forever()
104
except KeyboardInterrupt:
108
def db_publisher(report):
109
"""Publish OOPS reports to the oops-tools django store."""
110
# the first publisher will either inherit or assign, so this should be
112
assert report['id'] is not None
113
# Some fallback methods could lead to duplicate paths into the DB: exit
114
# early if the OOPS is already loaded.
116
res = Oops.objects.get(oopsid__exact=report['id'])
117
except Oops.DoesNotExist:
118
res = parsed_oops_to_model_oops(
119
report, report['datedir_repo_filepath'])
124
def make_amqp_config(output_dir):
125
"""Create an OOPS Config for republishing amqp OOPSes.
127
An OOPS published to this config will be written to disk and then loaded
130
:param output_dir: The directory to write OOPSes too.
133
disk_publisher = oops_datedir_repo.DateDirRepo(
134
output_dir, inherit_id=True, stash_path=True)
135
config.publishers.append(disk_publisher.publish)
136
config.publishers.append(db_publisher)