~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).
5
4935.3.7 by Curtis Hovey
Added bad name suppression to cronscripts.
6
# pylint: disable-msg=C0103,W0403
2908.4.9 by Guilherme Salgado
Implementation of the mirror prober and a bunch other things
7
8
"""Script to probe distribution mirrors and check how up-to-date they are."""
9
10
import _pythonpath
11
3691.214.40 by Guilherme Salgado
Fix a bunch of trivial mirror prober bugs: 46662, 68395 and 107473
12
import os
2908.4.12 by Guilherme Salgado
Lots of fixes Andrew suggested, fixed DB patch from Stuart and some tests to the mirror prober.
13
from StringIO import StringIO
2908.4.9 by Guilherme Salgado
Implementation of the mirror prober and a bunch other things
14
3691.217.1 by Guilherme Salgado
Fix https://launchpad.net/products/launchpad/+bug/54791 (The mirror prober should check a few files from each mirror in paralel instead of a lot of files from a single mirror)
15
from twisted.internet import reactor
2908.4.9 by Guilherme Salgado
Implementation of the mirror prober and a bunch other things
16
17
from zope.component import getUtility
18
19
from canonical.config import config
5842.1.1 by James Henstridge
Rename the transaction isolation level constants to match the psycopg2
20
from canonical.database.sqlbase import ISOLATION_LEVEL_AUTOCOMMIT
8356.1.1 by Leonard Richardson
Partial move.
21
from lp.services.scripts.base import (
4264.2.1 by James Henstridge
add a LaunchpadCronScript subclass, and make cronscripts/*.py use it
22
    LaunchpadCronScript, LaunchpadScriptFailure)
2908.4.12 by Guilherme Salgado
Lots of fixes Andrew suggested, fixed DB patch from Stuart and some tests to the mirror prober.
23
from canonical.launchpad.interfaces import (
3691.9.91 by Guilherme Salgado
Move distributionmirror-related dbschemas to DBEnumeratedTypes
24
    IDistributionMirrorSet, ILibraryFileAliasSet, MirrorContent)
3691.214.22 by Guilherme Salgado
Add an option to the mirror-prober script to not send notification to mirror owners.
25
from canonical.launchpad.webapp import canonical_url
8322.4.1 by Curtis Hovey
Moved the distributionmirror objects to lp.registry.
26
from lp.registry.scripts.distributionmirror_prober import (
4285.2.5 by Mark Shuttleworth
Test fixes for renamed series
27
    get_expected_cdimage_paths, probe_archive_mirror, probe_cdimage_mirror)
3525.1.1 by Guilherme Salgado
Make the mirror prober probe release mirrors, improve the UI a lot and lots of other fixes.
28
29
4264.2.1 by James Henstridge
add a LaunchpadCronScript subclass, and make cronscripts/*.py use it
30
class DistroMirrorProber(LaunchpadCronScript):
4285.2.5 by Mark Shuttleworth
Test fixes for renamed series
31
    usage = ('%prog --content-type=(archive|cdimage) [--force] '
4687.1.1 by Guilherme Salgado
Implement a limit to the number of mirrors we probe. (Fix https://bugs.launchpad.net/launchpad/+bug/123954)
32
             '[--no-owner-notification] [--max-mirrors=N]')
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
33
34
    def _sanity_check_mirror(self, mirror):
7222.2.4 by Curtis Hovey
Happify lint.
35
        """Check that the given mirror is official and has an http_base_url.
36
        """
37
        assert mirror.isOfficial(), (
38
            'Non-official mirrors should not be probed')
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
39
        if mirror.base_url is None:
40
            self.logger.warning(
41
                "Mirror '%s' of distribution '%s' doesn't have a base URL; "
7222.2.4 by Curtis Hovey
Happify lint.
42
                "we can't probe it." % (
43
                    mirror.name, mirror.distribution.name))
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
44
            return False
45
        return True
46
47
    def _create_probe_record(self, mirror, logfile):
7222.2.4 by Curtis Hovey
Happify lint.
48
        """Create a probe record for the given mirror with the given logfile.
49
        """
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
50
        logfile.seek(0)
51
        filename = '%s-probe-logfile.txt' % mirror.name
52
        log_file = getUtility(ILibraryFileAliasSet).create(
53
            name=filename, size=len(logfile.getvalue()),
54
            file=logfile, contentType='text/plain')
55
        mirror.newProbeRecord(log_file)
56
57
    def add_my_options(self):
58
        self.parser.add_option('--content-type',
59
            dest='content_type', default=None, action='store',
60
            help='Probe only mirrors of the given type')
61
        self.parser.add_option('--force',
62
            dest='force', default=False, action='store_true',
7222.2.4 by Curtis Hovey
Happify lint.
63
            help='Force the probing of mirrors that were probed recently')
3691.214.22 by Guilherme Salgado
Add an option to the mirror-prober script to not send notification to mirror owners.
64
        self.parser.add_option('--no-owner-notification',
65
            dest='no_owner_notification', default=False, action='store_true',
66
            help='Do not send failure notification to mirror owners.')
3691.214.24 by Guilherme Salgado
Implement the new +countrymirrors-archive page, together with some fixes to its underlying bits, new/improved tests and a new argument to the prober script, which causes it to not connect to anything other than localhost
67
        self.parser.add_option('--no-remote-hosts',
68
            dest='no_remote_hosts', default=False, action='store_true',
69
            help='Do not try to connect to any host other than localhost.')
4687.1.1 by Guilherme Salgado
Implement a limit to the number of mirrors we probe. (Fix https://bugs.launchpad.net/launchpad/+bug/123954)
70
        self.parser.add_option('--max-mirrors',
71
            dest='max_mirrors', default=None, action='store', type="int",
72
            help='Only probe N mirrors.')
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
73
74
    def main(self):
75
        if self.options.content_type == 'archive':
76
            probe_function = probe_archive_mirror
77
            content_type = MirrorContent.ARCHIVE
4285.2.5 by Mark Shuttleworth
Test fixes for renamed series
78
        elif self.options.content_type == 'cdimage':
79
            probe_function = probe_cdimage_mirror
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
80
            content_type = MirrorContent.RELEASE
81
        else:
82
            raise LaunchpadScriptFailure(
83
                'Wrong value for argument --content-type: %s'
84
                % self.options.content_type)
85
3691.214.40 by Guilherme Salgado
Fix a bunch of trivial mirror prober bugs: 46662, 68395 and 107473
86
        orig_proxy = os.environ.get('http_proxy')
87
        if config.distributionmirrorprober.use_proxy:
88
            os.environ['http_proxy'] = config.launchpad.http_proxy
3691.214.42 by Guilherme Salgado
No need to restore on the mirror prober cronscript
89
            self.logger.debug("Using %s as proxy." % os.environ['http_proxy'])
90
        else:
91
            self.logger.debug("Not using any proxy.")
3691.214.40 by Guilherme Salgado
Fix a bunch of trivial mirror prober bugs: 46662, 68395 and 107473
92
5821.19.2 by James Henstridge
distributionmirror-prober.py was twiddling the config object directly,
93
        self.txn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
94
        self.txn.begin()
95
3691.214.27 by Guilherme Salgado
A bunch of changes requested by Bjorn
96
        # Using a script argument to control a config variable is not a great
97
        # idea, but to me this seems better than passing the no_remote_hosts
98
        # value through a lot of method/function calls, until it reaches the
99
        # probe() method.
3691.214.24 by Guilherme Salgado
Implement the new +countrymirrors-archive page, together with some fixes to its underlying bits, new/improved tests and a new argument to the prober script, which causes it to not connect to anything other than localhost
100
        if self.options.no_remote_hosts:
7222.2.2 by Curtis Hovey
Fixed callsite that mutates the config.
101
            localhost_only_conf = """
102
                [distributionmirrorprober]
103
                localhost_only: True
104
                """
105
            config.push('localhost_only_conf', localhost_only_conf)
3691.214.24 by Guilherme Salgado
Implement the new +countrymirrors-archive page, together with some fixes to its underlying bits, new/improved tests and a new argument to the prober script, which causes it to not connect to anything other than localhost
106
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
107
        self.logger.info('Probing %s Mirrors' % content_type.title)
108
109
        mirror_set = getUtility(IDistributionMirrorSet)
110
111
        results = mirror_set.getMirrorsToProbe(
4687.1.1 by Guilherme Salgado
Implement a limit to the number of mirrors we probe. (Fix https://bugs.launchpad.net/launchpad/+bug/123954)
112
            content_type, ignore_last_probe=self.options.force,
113
            limit=self.options.max_mirrors)
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
114
        mirror_ids = [mirror.id for mirror in results]
3691.348.15 by kiko
Merge from RF (god damned conflict)
115
        unchecked_keys = []
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
116
        logfiles = {}
117
        probed_mirrors = []
118
119
        for mirror_id in mirror_ids:
120
            mirror = mirror_set[mirror_id]
121
            if not self._sanity_check_mirror(mirror):
122
                continue
123
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
124
            # XXX: salgado 2006-05-26:
125
            # Some people registered mirrors on distros other than Ubuntu back
126
            # in the old times, so now we need to do this small hack here.
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
127
            if not mirror.distribution.full_functionality:
128
                self.logger.info(
7222.2.4 by Curtis Hovey
Happify lint.
129
                    "Mirror '%s' of distribution '%s' can't be probed --we "
130
                    "only probe Ubuntu mirrors."
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
131
                    % (mirror.name, mirror.distribution.name))
132
                continue
133
134
            probed_mirrors.append(mirror)
135
            logfile = StringIO()
136
            logfiles[mirror_id] = logfile
3691.348.15 by kiko
Merge from RF (god damned conflict)
137
            probe_function(mirror, logfile, unchecked_keys, self.logger)
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
138
139
        if probed_mirrors:
140
            reactor.run()
141
            self.logger.info('Probed %d mirrors.' % len(probed_mirrors))
142
        else:
143
            self.logger.info('No mirrors to probe.')
144
3691.214.43 by Guilherme Salgado
Make the mirror prober more robust by simply logging unexpected exceptions rather than re-raising them and also make it more verbose.
145
        disabled_mirrors = []
146
        reenabled_mirrors = []
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
147
        # Now that we finished probing all mirrors, we check if any of these
148
        # mirrors appear to have no content mirrored, and, if so, mark them as
149
        # disabled and notify their owners.
150
        expected_iso_images_count = len(get_expected_cdimage_paths())
3691.214.22 by Guilherme Salgado
Add an option to the mirror-prober script to not send notification to mirror owners.
151
        notify_owner = not self.options.no_owner_notification
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
152
        for mirror in probed_mirrors:
5447.3.1 by Guilherme Salgado
Fix the bug
153
            log = logfiles[mirror.id]
154
            self._create_probe_record(mirror, log)
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
155
            if mirror.shouldDisable(expected_iso_images_count):
156
                if mirror.enabled:
5447.3.1 by Guilherme Salgado
Fix the bug
157
                    log.seek(0)
158
                    mirror.disable(notify_owner, log.getvalue())
3691.214.43 by Guilherme Salgado
Make the mirror prober more robust by simply logging unexpected exceptions rather than re-raising them and also make it more verbose.
159
                    disabled_mirrors.append(canonical_url(mirror))
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
160
            else:
161
                # Ensure the mirror is enabled, so that it shows up on public
162
                # mirror listings.
163
                if not mirror.enabled:
164
                    mirror.enabled = True
3691.214.43 by Guilherme Salgado
Make the mirror prober more robust by simply logging unexpected exceptions rather than re-raising them and also make it more verbose.
165
                    reenabled_mirrors.append(canonical_url(mirror))
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
166
3691.214.43 by Guilherme Salgado
Make the mirror prober more robust by simply logging unexpected exceptions rather than re-raising them and also make it more verbose.
167
        if disabled_mirrors:
168
            self.logger.info(
169
                'Disabling %s mirror(s): %s'
170
                % (len(disabled_mirrors), ", ".join(disabled_mirrors)))
171
        if reenabled_mirrors:
172
            self.logger.info(
173
                'Re-enabling %s mirror(s): %s'
174
                % (len(reenabled_mirrors), ", ".join(reenabled_mirrors)))
4664.1.1 by Curtis Hovey
Normalized comments for bug 3732.
175
        # XXX: salgado 2007-04-03:
5842.1.1 by James Henstridge
Rename the transaction isolation level constants to match the psycopg2
176
        # This should be done in LaunchpadScript.lock_and_run() when
177
        # the isolation used is ISOLATION_LEVEL_AUTOCOMMIT. Also note
178
        # that replacing this with a flush_database_updates() doesn't
179
        # have the same effect, it seems.
3691.214.36 by Guilherme Salgado
Revert a change to run the prober in autocommit isolation which was left here by accident
180
        self.txn.commit()
3691.214.40 by Guilherme Salgado
Fix a bunch of trivial mirror prober bugs: 46662, 68395 and 107473
181
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
182
        self.logger.info('Done.')
2908.4.9 by Guilherme Salgado
Implementation of the mirror prober and a bunch other things
183
184
185
if __name__ == '__main__':
3691.348.13 by kiko
Convert a couple of cronscripts over to LaunchpadScript
186
    script = DistroMirrorProber('distributionmirror-prober',
187
                                dbuser=config.distributionmirrorprober.dbuser)
188
    script.lock_and_run()
2908.4.9 by Guilherme Salgado
Implementation of the mirror prober and a bunch other things
189