Merge lp:~brian-murray/launchpad/api-export-messages-count into lp:launchpad/db-devel
- api-export-messages-count
- Merge into db-devel
Proposed by
Brian Murray
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~brian-murray/launchpad/api-export-messages-count | ||||
Merge into: | lp:launchpad/db-devel | ||||
Diff against target: |
1855 lines (+581/-358) 42 files modified
configs/development/launchpad-lazr.conf (+0/-2) configs/replicated-development/launchpad-lazr.conf (+0/-3) configs/test-playground/launchpad-lazr.conf (+0/-2) configs/testrunner/launchpad-lazr.conf (+0/-2) database/schema/security.cfg (+6/-2) lib/canonical/config/__init__.py (+2/-2) lib/canonical/config/schema-lazr.conf (+0/-8) lib/canonical/configure.zcml (+0/-2) lib/canonical/database/harness.py (+1/-2) lib/canonical/database/sqlbase.py (+2/-4) lib/canonical/database/tests/test_zopeless_transaction_manager.py (+0/-31) lib/canonical/launchpad/database/tests/test_oauth.py (+1/-1) lib/canonical/launchpad/doc/account.txt (+1/-26) lib/canonical/launchpad/doc/storm.txt (+51/-71) lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt (+0/-2) lib/canonical/launchpad/scripts/garbo.py (+5/-13) lib/canonical/launchpad/scripts/tests/test_garbo.py (+7/-9) lib/canonical/launchpad/webapp/adapter.py (+5/-16) lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt (+1/-24) lib/canonical/launchpad/webapp/interfaces.py (+1/-3) lib/canonical/launchpad/webapp/tests/test_dbpolicy.py (+1/-4) lib/lp/bugs/doc/checkwatches.txt (+4/-0) lib/lp/bugs/doc/externalbugtracker-comment-imports.txt (+3/-3) lib/lp/bugs/interfaces/bug.py (+3/-3) lib/lp/bugs/stories/webservice/xx-bug.txt (+1/-0) lib/lp/code/doc/branch-karma.txt (+1/-0) lib/lp/registry/configure.zcml (+47/-5) lib/lp/registry/doc/distribution-mirror.txt (+119/-1) lib/lp/registry/interfaces/distribution.py (+9/-1) lib/lp/registry/interfaces/distributionmirror.py (+96/-47) lib/lp/registry/model/distribution.py (+11/-0) lib/lp/registry/model/distributionmirror.py (+63/-1) lib/lp/registry/model/person.py (+2/-17) lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt (+11/-11) lib/lp/registry/stories/person/xx-admin-person-review.txt (+1/-1) lib/lp/registry/stories/webservice/xx-distribution-mirror.txt (+20/-11) lib/lp/registry/stories/webservice/xx-distribution.txt (+74/-0) lib/lp/registry/tests/test_distributionmirror.py (+4/-14) lib/lp/registry/tests/test_personset.py (+6/-10) lib/lp/testing/factory.py (+16/-2) lib/lp/testopenid/browser/server.py (+6/-1) utilities/sourcedeps.conf (+0/-1) |
||||
To merge this branch: | bzr merge lp:~brian-murray/launchpad/api-export-messages-count | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | Approve | ||
Review via email: mp+17697@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Brian Murray (brian-murray) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'configs/development/launchpad-lazr.conf' | |||
2 | --- configs/development/launchpad-lazr.conf 2010-03-05 20:49:31 +0000 | |||
3 | +++ configs/development/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 | |||
4 | @@ -97,8 +97,6 @@ | |||
5 | 97 | # the rw_* configs. | 97 | # the rw_* configs. |
6 | 98 | ro_main_master: dbname=launchpad_dev_template | 98 | ro_main_master: dbname=launchpad_dev_template |
7 | 99 | ro_main_slave: dbname=launchpad_dev_template | 99 | ro_main_slave: dbname=launchpad_dev_template |
8 | 100 | auth_master: dbname=launchpad_dev | ||
9 | 101 | auth_slave: dbname=launchpad_dev | ||
10 | 102 | 100 | ||
11 | 103 | [distributionmirrorprober] | 101 | [distributionmirrorprober] |
12 | 104 | use_proxy: False | 102 | use_proxy: False |
13 | 105 | 103 | ||
14 | === modified file 'configs/replicated-development/launchpad-lazr.conf' | |||
15 | --- configs/replicated-development/launchpad-lazr.conf 2010-01-05 19:09:58 +0000 | |||
16 | +++ configs/replicated-development/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 | |||
17 | @@ -10,6 +10,3 @@ | |||
18 | 10 | rw_main_slave: dbname=launchpad_dev_slave | 10 | rw_main_slave: dbname=launchpad_dev_slave |
19 | 11 | ro_main_master: dbname=launchpad_dev | 11 | ro_main_master: dbname=launchpad_dev |
20 | 12 | ro_main_slave: dbname=launchpad_dev_slave | 12 | ro_main_slave: dbname=launchpad_dev_slave |
21 | 13 | auth_master: dbname=launchpad_dev | ||
22 | 14 | auth_slave: dbname=launchpad_dev_slave | ||
23 | 15 | |||
24 | 16 | 13 | ||
25 | === modified file 'configs/test-playground/launchpad-lazr.conf' | |||
26 | --- configs/test-playground/launchpad-lazr.conf 2010-01-05 19:09:58 +0000 | |||
27 | +++ configs/test-playground/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 | |||
28 | @@ -10,5 +10,3 @@ | |||
29 | 10 | rw_main_slave: dbname=launchpad_ftest_playground | 10 | rw_main_slave: dbname=launchpad_ftest_playground |
30 | 11 | ro_main_master: dbname=launchpad_ftest_playground | 11 | ro_main_master: dbname=launchpad_ftest_playground |
31 | 12 | ro_main_slave: dbname=launchpad_ftest_playground | 12 | ro_main_slave: dbname=launchpad_ftest_playground |
32 | 13 | auth_master: dbname=launchpad_ftest_playground | ||
33 | 14 | auth_slave: dbname=launchpad_ftest_playground | ||
34 | 15 | 13 | ||
35 | === modified file 'configs/testrunner/launchpad-lazr.conf' | |||
36 | --- configs/testrunner/launchpad-lazr.conf 2010-03-11 01:39:25 +0000 | |||
37 | +++ configs/testrunner/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 | |||
38 | @@ -51,8 +51,6 @@ | |||
39 | 51 | # the rw_* configs. | 51 | # the rw_* configs. |
40 | 52 | ro_main_master: dbname=launchpad_ftest_template | 52 | ro_main_master: dbname=launchpad_ftest_template |
41 | 53 | ro_main_slave: dbname=launchpad_ftest_template | 53 | ro_main_slave: dbname=launchpad_ftest_template |
42 | 54 | auth_master: dbname=launchpad_ftest | ||
43 | 55 | auth_slave: dbname=launchpad_ftest | ||
44 | 56 | randomise_select_results: true | 54 | randomise_select_results: true |
45 | 57 | 55 | ||
46 | 58 | [error_reports] | 56 | [error_reports] |
47 | 59 | 57 | ||
48 | === modified file 'database/schema/security.cfg' | |||
49 | --- database/schema/security.cfg 2010-04-02 17:29:13 +0000 | |||
50 | +++ database/schema/security.cfg 2010-04-05 20:44:34 +0000 | |||
51 | @@ -118,7 +118,8 @@ | |||
52 | 118 | # lpmain replication set access from the main Z3 application. | 118 | # lpmain replication set access from the main Z3 application. |
53 | 119 | type=user | 119 | type=user |
54 | 120 | groups=write,script | 120 | groups=write,script |
56 | 121 | public.account = SELECT | 121 | public.account = SELECT, INSERT, UPDATE, DELETE |
57 | 122 | public.accountpassword = SELECT, INSERT, UPDATE, DELETE | ||
58 | 122 | public.announcement = SELECT, INSERT, UPDATE, DELETE | 123 | public.announcement = SELECT, INSERT, UPDATE, DELETE |
59 | 123 | public.answercontact = SELECT, INSERT, UPDATE, DELETE | 124 | public.answercontact = SELECT, INSERT, UPDATE, DELETE |
60 | 124 | public.apportjob = SELECT, INSERT, UPDATE, DELETE | 125 | public.apportjob = SELECT, INSERT, UPDATE, DELETE |
61 | @@ -176,7 +177,7 @@ | |||
62 | 176 | public.distributionsourcepackagecache = SELECT | 177 | public.distributionsourcepackagecache = SELECT |
63 | 177 | public.distroserieslanguage = SELECT, INSERT, UPDATE | 178 | public.distroserieslanguage = SELECT, INSERT, UPDATE |
64 | 178 | public.distroseriespackagecache = SELECT | 179 | public.distroseriespackagecache = SELECT |
66 | 179 | public.emailaddress = SELECT | 180 | public.emailaddress = SELECT, INSERT, UPDATE, DELETE |
67 | 180 | public.entitlement = SELECT, INSERT, UPDATE, DELETE | 181 | public.entitlement = SELECT, INSERT, UPDATE, DELETE |
68 | 181 | public.faq = SELECT, INSERT, UPDATE, DELETE | 182 | public.faq = SELECT, INSERT, UPDATE, DELETE |
69 | 182 | public.featuredproject = SELECT, INSERT, DELETE | 183 | public.featuredproject = SELECT, INSERT, DELETE |
70 | @@ -916,6 +917,8 @@ | |||
71 | 916 | # Full access except for tables that are exclusively updated by | 917 | # Full access except for tables that are exclusively updated by |
72 | 917 | # certain processes, such as the librarian tables. This group is deprecated - | 918 | # certain processes, such as the librarian tables. This group is deprecated - |
73 | 918 | # access should be explicitly granted to users. | 919 | # access should be explicitly granted to users. |
74 | 920 | public.account = SELECT, INSERT, UPDATE | ||
75 | 921 | public.accountpassword = SELECT, INSERT | ||
76 | 919 | public.archive = SELECT, INSERT, UPDATE | 922 | public.archive = SELECT, INSERT, UPDATE |
77 | 920 | public.archivearch = SELECT, INSERT, UPDATE, DELETE | 923 | public.archivearch = SELECT, INSERT, UPDATE, DELETE |
78 | 921 | public.binarypackagerelease = SELECT, INSERT, UPDATE | 924 | public.binarypackagerelease = SELECT, INSERT, UPDATE |
79 | @@ -949,6 +952,7 @@ | |||
80 | 949 | public.distribution = SELECT, INSERT, UPDATE | 952 | public.distribution = SELECT, INSERT, UPDATE |
81 | 950 | public.distroarchseries = SELECT, INSERT, UPDATE | 953 | public.distroarchseries = SELECT, INSERT, UPDATE |
82 | 951 | public.distroseries = SELECT, INSERT, UPDATE | 954 | public.distroseries = SELECT, INSERT, UPDATE |
83 | 955 | public.openidrpsummary = SELECT, INSERT, UPDATE | ||
84 | 952 | public.packageupload = SELECT, INSERT, UPDATE | 956 | public.packageupload = SELECT, INSERT, UPDATE |
85 | 953 | public.packageuploadbuild = SELECT, INSERT, UPDATE | 957 | public.packageuploadbuild = SELECT, INSERT, UPDATE |
86 | 954 | public.packageuploadsource = SELECT, INSERT, UPDATE | 958 | public.packageuploadsource = SELECT, INSERT, UPDATE |
87 | 955 | 959 | ||
88 | === modified file 'lib/canonical/config/__init__.py' | |||
89 | --- lib/canonical/config/__init__.py 2010-01-14 16:39:18 +0000 | |||
90 | +++ lib/canonical/config/__init__.py 2010-04-05 20:44:34 +0000 | |||
91 | @@ -374,13 +374,13 @@ | |||
92 | 374 | _db_config_attrs = frozenset([ | 374 | _db_config_attrs = frozenset([ |
93 | 375 | 'dbuser', 'auth_dbuser', | 375 | 'dbuser', 'auth_dbuser', |
94 | 376 | 'rw_main_master', 'rw_main_slave', | 376 | 'rw_main_master', 'rw_main_slave', |
96 | 377 | 'ro_main_master', 'ro_main_slave', 'auth_master', 'auth_slave', | 377 | 'ro_main_master', 'ro_main_slave', |
97 | 378 | 'db_statement_timeout', 'db_statement_timeout_precision', | 378 | 'db_statement_timeout', 'db_statement_timeout_precision', |
98 | 379 | 'isolation_level', 'randomise_select_results', | 379 | 'isolation_level', 'randomise_select_results', |
99 | 380 | 'soft_request_timeout', 'storm_cache', 'storm_cache_size']) | 380 | 'soft_request_timeout', 'storm_cache', 'storm_cache_size']) |
100 | 381 | _db_config_required_attrs = frozenset([ | 381 | _db_config_required_attrs = frozenset([ |
101 | 382 | 'dbuser', 'rw_main_master', 'rw_main_slave', 'ro_main_master', | 382 | 'dbuser', 'rw_main_master', 'rw_main_slave', 'ro_main_master', |
103 | 383 | 'ro_main_slave', 'auth_master', 'auth_slave']) | 383 | 'ro_main_slave']) |
104 | 384 | 384 | ||
105 | 385 | @property | 385 | @property |
106 | 386 | def main_master(self): | 386 | def main_master(self): |
107 | 387 | 387 | ||
108 | === modified file 'lib/canonical/config/schema-lazr.conf' | |||
109 | --- lib/canonical/config/schema-lazr.conf 2010-03-24 02:37:55 +0000 | |||
110 | +++ lib/canonical/config/schema-lazr.conf 2010-04-05 20:44:34 +0000 | |||
111 | @@ -559,8 +559,6 @@ | |||
112 | 559 | rw_main_slave: dbname=launchpad_prod_2 host=chokecherry.canonical.com | 559 | rw_main_slave: dbname=launchpad_prod_2 host=chokecherry.canonical.com |
113 | 560 | ro_main_master: dbname=launchpad_standalone_1 host=chokecherry.canonical.com | 560 | ro_main_master: dbname=launchpad_standalone_1 host=chokecherry.canonical.com |
114 | 561 | ro_main_slave: dbname=launchpad_standalone_1 host=chokecherry.canonical.com | 561 | ro_main_slave: dbname=launchpad_standalone_1 host=chokecherry.canonical.com |
115 | 562 | auth_master: dbname=launchpad_prod_3 host=wildcherry.canonical.com | ||
116 | 563 | auth_slave: dbname=launchpad_prod_2 host=chokecherry.canonical.com | ||
117 | 564 | 562 | ||
118 | 565 | # If the replication lag is more than this many seconds, slave databases | 563 | # If the replication lag is more than this many seconds, slave databases |
119 | 566 | # will not be used. | 564 | # will not be used. |
120 | @@ -867,16 +865,10 @@ | |||
121 | 867 | # datatype: integer | 865 | # datatype: integer |
122 | 868 | max_scaling: 500 | 866 | max_scaling: 500 |
123 | 869 | 867 | ||
124 | 870 | [sso] | ||
125 | 871 | dbuser: sso_main | ||
126 | 872 | auth_dbuser: sso_auth | ||
127 | 873 | |||
128 | 874 | |||
129 | 875 | [launchpad] | 868 | [launchpad] |
130 | 876 | # The database user which will be used by this process. | 869 | # The database user which will be used by this process. |
131 | 877 | # datatype: string | 870 | # datatype: string |
132 | 878 | dbuser: launchpad_main | 871 | dbuser: launchpad_main |
133 | 879 | auth_dbuser: launchpad_auth | ||
134 | 880 | storm_cache: generational | 872 | storm_cache: generational |
135 | 881 | storm_cache_size: 10000 | 873 | storm_cache_size: 10000 |
136 | 882 | 874 | ||
137 | 883 | 875 | ||
138 | === modified file 'lib/canonical/configure.zcml' | |||
139 | --- lib/canonical/configure.zcml 2010-01-08 21:23:15 +0000 | |||
140 | +++ lib/canonical/configure.zcml 2010-04-05 20:44:34 +0000 | |||
141 | @@ -154,7 +154,5 @@ | |||
142 | 154 | <include package="canonical.lazr" /> | 154 | <include package="canonical.lazr" /> |
143 | 155 | <include zcml:condition="installed canonical.shipit" | 155 | <include zcml:condition="installed canonical.shipit" |
144 | 156 | package="canonical.shipit" /> | 156 | package="canonical.shipit" /> |
145 | 157 | <include zcml:condition="installed canonical.signon" | ||
146 | 158 | package="canonical.signon" /> | ||
147 | 159 | 157 | ||
148 | 160 | </configure> | 158 | </configure> |
149 | 161 | 159 | ||
150 | === modified file 'lib/canonical/database/harness.py' | |||
151 | --- lib/canonical/database/harness.py 2009-11-05 03:52:51 +0000 | |||
152 | +++ lib/canonical/database/harness.py 2010-04-05 20:44:34 +0000 | |||
153 | @@ -48,8 +48,7 @@ | |||
154 | 48 | from storm.locals import * | 48 | from storm.locals import * |
155 | 49 | from storm.expr import * | 49 | from storm.expr import * |
156 | 50 | from canonical.launchpad.webapp.interfaces import ( | 50 | from canonical.launchpad.webapp.interfaces import ( |
159 | 51 | IStoreSelector, MAIN_STORE, AUTH_STORE, MASTER_FLAVOR, | 51 | IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR, DEFAULT_FLAVOR) |
158 | 52 | SLAVE_FLAVOR, DEFAULT_FLAVOR) | ||
160 | 53 | 52 | ||
161 | 54 | 53 | ||
162 | 55 | def switch_db_user(dbuser, commit_first=True): | 54 | def switch_db_user(dbuser, commit_first=True): |
163 | 56 | 55 | ||
164 | === modified file 'lib/canonical/database/sqlbase.py' | |||
165 | --- lib/canonical/database/sqlbase.py 2010-03-23 15:04:41 +0000 | |||
166 | +++ lib/canonical/database/sqlbase.py 2010-04-05 20:44:34 +0000 | |||
167 | @@ -271,7 +271,6 @@ | |||
168 | 271 | # This is only used by scripts, so we must connect to the read-write | 271 | # This is only used by scripts, so we must connect to the read-write |
169 | 272 | # DB here -- that's why we use rw_main_master directly. | 272 | # DB here -- that's why we use rw_main_master directly. |
170 | 273 | main_connection_string = dbconfig.rw_main_master | 273 | main_connection_string = dbconfig.rw_main_master |
171 | 274 | auth_connection_string = dbconfig.auth_master | ||
172 | 275 | 274 | ||
173 | 276 | # Override dbname and dbhost in the connection string if they | 275 | # Override dbname and dbhost in the connection string if they |
174 | 277 | # have been passed in. | 276 | # have been passed in. |
175 | @@ -290,7 +289,7 @@ | |||
176 | 290 | match = re.search(r'host=(\S*)', main_connection_string) | 289 | match = re.search(r'host=(\S*)', main_connection_string) |
177 | 291 | if match is not None: | 290 | if match is not None: |
178 | 292 | dbhost = match.group(1) | 291 | dbhost = match.group(1) |
180 | 293 | return main_connection_string, auth_connection_string, dbname, dbhost | 292 | return main_connection_string, dbname, dbhost |
181 | 294 | 293 | ||
182 | 295 | @classmethod | 294 | @classmethod |
183 | 296 | def initZopeless(cls, dbname=None, dbhost=None, dbuser=None, | 295 | def initZopeless(cls, dbname=None, dbhost=None, dbuser=None, |
184 | @@ -298,7 +297,7 @@ | |||
185 | 298 | # Connect to the auth master store as well, as some scripts might need | 297 | # Connect to the auth master store as well, as some scripts might need |
186 | 299 | # to create EmailAddresses and Accounts. | 298 | # to create EmailAddresses and Accounts. |
187 | 300 | 299 | ||
189 | 301 | main_connection_string, auth_connection_string, dbname, dbhost = ( | 300 | main_connection_string, dbname, dbhost = ( |
190 | 302 | cls._get_zopeless_connection_config(dbname, dbhost)) | 301 | cls._get_zopeless_connection_config(dbname, dbhost)) |
191 | 303 | 302 | ||
192 | 304 | assert dbuser is not None, ''' | 303 | assert dbuser is not None, ''' |
193 | @@ -315,7 +314,6 @@ | |||
194 | 315 | overlay = dedent("""\ | 314 | overlay = dedent("""\ |
195 | 316 | [database] | 315 | [database] |
196 | 317 | rw_main_master: %(main_connection_string)s | 316 | rw_main_master: %(main_connection_string)s |
197 | 318 | auth_master: %(auth_connection_string)s | ||
198 | 319 | isolation_level: %(isolation_level)s | 317 | isolation_level: %(isolation_level)s |
199 | 320 | """ % vars()) | 318 | """ % vars()) |
200 | 321 | 319 | ||
201 | 322 | 320 | ||
202 | === modified file 'lib/canonical/database/tests/test_zopeless_transaction_manager.py' | |||
203 | --- lib/canonical/database/tests/test_zopeless_transaction_manager.py 2009-12-14 17:38:34 +0000 | |||
204 | +++ lib/canonical/database/tests/test_zopeless_transaction_manager.py 2010-04-05 20:44:34 +0000 | |||
205 | @@ -1,48 +1,17 @@ | |||
206 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the |
207 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
208 | 3 | 3 | ||
209 | 4 | from textwrap import dedent | ||
210 | 5 | import unittest | 4 | import unittest |
211 | 6 | 5 | ||
212 | 7 | from zope.component import getUtility | 6 | from zope.component import getUtility |
213 | 8 | 7 | ||
214 | 9 | from storm.zope.interfaces import IZStorm | 8 | from storm.zope.interfaces import IZStorm |
215 | 10 | 9 | ||
216 | 11 | from canonical.config import config | ||
217 | 12 | from canonical.database.sqlbase import ZopelessTransactionManager | 10 | from canonical.database.sqlbase import ZopelessTransactionManager |
218 | 13 | from canonical.testing.layers import LaunchpadZopelessLayer | 11 | from canonical.testing.layers import LaunchpadZopelessLayer |
219 | 14 | from lp.testing import TestCase | 12 | from lp.testing import TestCase |
220 | 15 | 13 | ||
221 | 16 | 14 | ||
222 | 17 | class TestZopelessTransactionManagerNoLayer(TestCase): | ||
223 | 18 | |||
224 | 19 | def test_initZopeless_connects_to_auth_master_db(self): | ||
225 | 20 | # Some scripts might create EmailAddress and Account entries, so | ||
226 | 21 | # initZopeless has to connect to the auth master db. This is a | ||
227 | 22 | # bugfix test. The error that this test detects is that the | ||
228 | 23 | # script used to use the main_master database for the | ||
229 | 24 | # auth_master. In this test, we make sure that the auth_master | ||
230 | 25 | # and main_master have different values in the config, and then | ||
231 | 26 | # show that they are honored. Prior to the fix, ``auth_master`` | ||
232 | 27 | # would have been changed to the same value as ``main_master``. | ||
233 | 28 | # Now we set up our test data and push it on the config. | ||
234 | 29 | auth_master = "dbname=example_launchpad_auth_does_not_exist" | ||
235 | 30 | overlay = dedent(""" | ||
236 | 31 | [database] | ||
237 | 32 | main_master: dbname=launchpad_dev | ||
238 | 33 | auth_master: %s | ||
239 | 34 | """ % (auth_master,)) | ||
240 | 35 | config.push('new-db', overlay) | ||
241 | 36 | try: | ||
242 | 37 | main_connection_string, auth_connection_string, dbname, dbhost = ( | ||
243 | 38 | ZopelessTransactionManager._get_zopeless_connection_config( | ||
244 | 39 | None, None)) | ||
245 | 40 | self.assertEqual(auth_connection_string, auth_master) | ||
246 | 41 | finally: | ||
247 | 42 | # Clean up the configuration | ||
248 | 43 | config.pop('new-db') | ||
249 | 44 | |||
250 | 45 | |||
251 | 46 | class TestZopelessTransactionManager(TestCase): | 15 | class TestZopelessTransactionManager(TestCase): |
252 | 47 | layer = LaunchpadZopelessLayer | 16 | layer = LaunchpadZopelessLayer |
253 | 48 | 17 | ||
254 | 49 | 18 | ||
255 | === modified file 'lib/canonical/launchpad/database/tests/test_oauth.py' | |||
256 | --- lib/canonical/launchpad/database/tests/test_oauth.py 2009-06-25 05:30:52 +0000 | |||
257 | +++ lib/canonical/launchpad/database/tests/test_oauth.py 2010-04-05 20:44:34 +0000 | |||
258 | @@ -22,7 +22,7 @@ | |||
259 | 22 | """Base tests for the OAuth database classes.""" | 22 | """Base tests for the OAuth database classes.""" |
260 | 23 | layer = DatabaseFunctionalLayer | 23 | layer = DatabaseFunctionalLayer |
261 | 24 | 24 | ||
263 | 25 | def test__get_store_should_return_the_auth_master_store(self): | 25 | def test__get_store_should_return_the_main_master_store(self): |
264 | 26 | """We want all OAuth classes to use the master store. | 26 | """We want all OAuth classes to use the master store. |
265 | 27 | Otherwise, the OAuth exchanges will fail because the authorize | 27 | Otherwise, the OAuth exchanges will fail because the authorize |
266 | 28 | screen won't probably find the new request token on the slave store. | 28 | screen won't probably find the new request token on the slave store. |
267 | 29 | 29 | ||
268 | === modified file 'lib/canonical/launchpad/doc/account.txt' | |||
269 | --- lib/canonical/launchpad/doc/account.txt 2010-02-12 15:57:27 +0000 | |||
270 | +++ lib/canonical/launchpad/doc/account.txt 2010-04-05 20:44:34 +0000 | |||
271 | @@ -196,32 +196,6 @@ | |||
272 | 196 | >>> account.status = AccountStatus.ACTIVE | 196 | >>> account.status = AccountStatus.ACTIVE |
273 | 197 | >>> login('no-priv@canonical.com') | 197 | >>> login('no-priv@canonical.com') |
274 | 198 | 198 | ||
275 | 199 | The Account's displayname is synced to the Person's displayname if there | ||
276 | 200 | is one. If the Person.displayname is changed, the Account.displayname is | ||
277 | 201 | changed too. | ||
278 | 202 | |||
279 | 203 | >>> from canonical.launchpad.interfaces import IPersonSet | ||
280 | 204 | |||
281 | 205 | >>> personset = getUtility(IPersonSet) | ||
282 | 206 | >>> person = personset.getByEmail('no-priv@canonical.com') | ||
283 | 207 | >>> person.displayname = 'Something New' | ||
284 | 208 | >>> print account.displayname | ||
285 | 209 | Something New | ||
286 | 210 | |||
287 | 211 | However, the reverse is not true. If we change the Account.displayname, | ||
288 | 212 | the linked Person.displayname (if there is one) is not updated | ||
289 | 213 | immediately. Instead, a cron job will sync this information later. This | ||
290 | 214 | allows displayname changes to happen even when the Person table is | ||
291 | 215 | unavailable. | ||
292 | 216 | |||
293 | 217 | >>> account.displayname = 'No Privileges Account' | ||
294 | 218 | >>> print person.displayname | ||
295 | 219 | Something New | ||
296 | 220 | |||
297 | 221 | >>> person.displayname = 'No Privileges Person' | ||
298 | 222 | >>> print account.displayname | ||
299 | 223 | No Privileges Person | ||
300 | 224 | |||
301 | 225 | An Account has an OpenID identifier used to generate the OpenID identity | 199 | An Account has an OpenID identifier used to generate the OpenID identity |
302 | 226 | URL. | 200 | URL. |
303 | 227 | 201 | ||
304 | @@ -242,6 +216,7 @@ | |||
305 | 242 | >>> login('admin@canonical.com') | 216 | >>> login('admin@canonical.com') |
306 | 243 | >>> passwordless_account = account_set.new( | 217 | >>> passwordless_account = account_set.new( |
307 | 244 | ... AccountCreationRationale.USER_CREATED, 'Passwordless') | 218 | ... AccountCreationRationale.USER_CREATED, 'Passwordless') |
308 | 219 | >>> transaction.commit() | ||
309 | 245 | >>> print passwordless_account.creation_rationale.name | 220 | >>> print passwordless_account.creation_rationale.name |
310 | 246 | USER_CREATED | 221 | USER_CREATED |
311 | 247 | >>> print passwordless_account.displayname | 222 | >>> print passwordless_account.displayname |
312 | 248 | 223 | ||
313 | === modified file 'lib/canonical/launchpad/doc/storm.txt' | |||
314 | --- lib/canonical/launchpad/doc/storm.txt 2010-02-22 10:33:10 +0000 | |||
315 | +++ lib/canonical/launchpad/doc/storm.txt 2010-04-05 20:44:34 +0000 | |||
316 | @@ -7,10 +7,11 @@ | |||
317 | 7 | specific Storm tools to cope with our master and slave store arrangement. | 7 | specific Storm tools to cope with our master and slave store arrangement. |
318 | 8 | 8 | ||
319 | 9 | >>> from canonical.launchpad.interfaces import ( | 9 | >>> from canonical.launchpad.interfaces import ( |
321 | 10 | ... EmailAddressStatus, IAccountSet, IEmailAddressSet, | 10 | ... EmailAddressStatus, IEmailAddressSet, |
322 | 11 | ... IMasterObject, IMasterStore, ISlaveStore, IStore) | 11 | ... IMasterObject, IMasterStore, ISlaveStore, IStore) |
323 | 12 | >>> from canonical.launchpad.database import ( | 12 | >>> from canonical.launchpad.database import ( |
324 | 13 | ... Account, AccountPassword, EmailAddress) | 13 | ... Account, AccountPassword, EmailAddress) |
325 | 14 | >>> from lp.registry.interfaces.person import IPersonSet | ||
326 | 14 | >>> from lp.registry.model.person import Person | 15 | >>> from lp.registry.model.person import Person |
327 | 15 | >>> from zope.security.proxy import ProxyFactory | 16 | >>> from zope.security.proxy import ProxyFactory |
328 | 16 | 17 | ||
329 | @@ -19,30 +20,14 @@ | |||
330 | 19 | a Launchpad database object. You can use adapters to | 20 | a Launchpad database object. You can use adapters to |
331 | 20 | retrieve the correct Store. | 21 | retrieve the correct Store. |
332 | 21 | 22 | ||
333 | 22 | >>> auth_master = IMasterStore(Account) | ||
334 | 23 | >>> main_master = IMasterStore(Person) | 23 | >>> main_master = IMasterStore(Person) |
335 | 24 | >>> auth_master is main_master | ||
336 | 25 | False | ||
337 | 26 | |||
338 | 27 | |||
339 | 28 | You can read most tables from any Store, which is required for doing | ||
340 | 29 | fast joins in the database. However, when it is not necessary to | ||
341 | 30 | retrieve objects from the same store as another object, it is better to | ||
342 | 31 | explicitly use the explicit Store for its replication set. Some tables | ||
343 | 32 | are only available from this store, such as the AccountPassword table. | ||
344 | 33 | |||
345 | 34 | >>> auth_slave = ISlaveStore(AccountPassword) | ||
346 | 35 | >>> main_slave = ISlaveStore(Person) | ||
347 | 36 | >>> auth_slave is main_slave | ||
348 | 37 | False | ||
349 | 38 | |||
350 | 39 | 24 | ||
351 | 40 | You can detect if a store is writable by checking what interfaces it | 25 | You can detect if a store is writable by checking what interfaces it |
352 | 41 | provides. | 26 | provides. |
353 | 42 | 27 | ||
355 | 43 | >>> IMasterStore.providedBy(auth_master) | 28 | >>> IMasterStore.providedBy(main_master) |
356 | 44 | True | 29 | True |
358 | 45 | >>> ISlaveStore.providedBy(auth_master) | 30 | >>> ISlaveStore.providedBy(main_master) |
359 | 46 | False | 31 | False |
360 | 47 | 32 | ||
361 | 48 | 33 | ||
362 | @@ -53,7 +38,6 @@ | |||
363 | 53 | Otherwise, it gives you the master. See IStoreSelector for details. | 38 | Otherwise, it gives you the master. See IStoreSelector for details. |
364 | 54 | 39 | ||
365 | 55 | >>> main_default = IStore(Person) | 40 | >>> main_default = IStore(Person) |
366 | 56 | >>> main_master = IMasterStore(Person) | ||
367 | 57 | >>> main_slave = ISlaveStore(Person) | 41 | >>> main_slave = ISlaveStore(Person) |
368 | 58 | >>> main_default is main_master | 42 | >>> main_default is main_master |
369 | 59 | True | 43 | True |
370 | @@ -78,11 +62,10 @@ | |||
371 | 78 | changes to an object, just in case you have been passed an instance | 62 | changes to an object, just in case you have been passed an instance |
372 | 79 | from a store other than the correct Master. | 63 | from a store other than the correct Master. |
373 | 80 | 64 | ||
375 | 81 | >>> auth_slave = ISlaveStore(Account) | 65 | >>> main_slave = ISlaveStore(Person) |
376 | 82 | >>> t = transaction.begin() | 66 | >>> t = transaction.begin() |
380 | 83 | >>> account = auth_slave.find( | 67 | >>> person = main_slave.find(Person, name='mark').one() |
381 | 84 | ... Account, openid_identifier='mark_oid').one() | 68 | >>> person.displayname = 'Cannot change' |
379 | 85 | >>> account.displayname = 'Cannot change' | ||
382 | 86 | >>> transaction.commit() | 69 | >>> transaction.commit() |
383 | 87 | Traceback (most recent call last): | 70 | Traceback (most recent call last): |
384 | 88 | ... | 71 | ... |
385 | @@ -90,9 +73,8 @@ | |||
386 | 90 | 73 | ||
387 | 91 | >>> transaction.abort() | 74 | >>> transaction.abort() |
388 | 92 | >>> t = transaction.begin() | 75 | >>> t = transaction.begin() |
392 | 93 | >>> account = auth_slave.find( | 76 | >>> person = main_slave.find(Person, name='mark').one() |
393 | 94 | ... Account, openid_identifier='mark_oid').one() | 77 | >>> IMasterObject(person).displayname = 'Can change' |
391 | 95 | >>> IMasterObject(account).displayname = 'Can change' | ||
394 | 96 | >>> transaction.commit() | 78 | >>> transaction.commit() |
395 | 97 | 79 | ||
396 | 98 | 80 | ||
397 | @@ -100,61 +82,60 @@ | |||
398 | 100 | similarly wrapped. | 82 | similarly wrapped. |
399 | 101 | 83 | ||
400 | 102 | >>> from zope.security.proxy import removeSecurityProxy | 84 | >>> from zope.security.proxy import removeSecurityProxy |
423 | 103 | >>> account = getUtility(IAccountSet).getByEmail('no-priv@canonical.com') | 85 | >>> person = getUtility(IPersonSet).getByEmail('no-priv@canonical.com') |
424 | 104 | >>> removeSecurityProxy(account) is account | 86 | >>> removeSecurityProxy(person) is person |
425 | 105 | False | 87 | False |
426 | 106 | >>> account.displayname | 88 | >>> person.displayname |
427 | 107 | u'No Privileges Person' | 89 | u'No Privileges Person' |
428 | 108 | >>> account.password | 90 | >>> person.name = 'foo' |
429 | 109 | Traceback (most recent call last): | 91 | Traceback (most recent call last): |
430 | 110 | ... | 92 | ... |
431 | 111 | Unauthorized: ... | 93 | Unauthorized: ... |
432 | 112 | 94 | ||
433 | 113 | >>> account = IMasterObject(account) | 95 | >>> person = IMasterObject(person) |
434 | 114 | >>> removeSecurityProxy(account) is account | 96 | >>> removeSecurityProxy(person) is person |
435 | 115 | False | 97 | False |
436 | 116 | >>> account.displayname | 98 | >>> person.displayname |
437 | 117 | u'No Privileges Person' | 99 | u'No Privileges Person' |
438 | 118 | >>> account.password | 100 | >>> person.name = 'foo' |
439 | 119 | Traceback (most recent call last): | 101 | Traceback (most recent call last): |
440 | 120 | ... | 102 | ... |
441 | 121 | Unauthorized: ... | 103 | Unauthorized: ... |
442 | 122 | 104 | ||
443 | 123 | >>> account = IMasterObject(removeSecurityProxy(account)) | 105 | >>> person = IMasterObject(removeSecurityProxy(person)) |
444 | 124 | >>> removeSecurityProxy(account) is account | 106 | >>> removeSecurityProxy(person) is person |
445 | 125 | True | 107 | True |
447 | 126 | >>> account.displayname | 108 | >>> person.displayname |
448 | 127 | u'No Privileges Person' | 109 | u'No Privileges Person' |
451 | 128 | >>> account.password | 110 | >>> person.name = 'foo' |
450 | 129 | u'...' | ||
452 | 130 | 111 | ||
453 | 131 | Our objects may compare equal even if they have come from different | 112 | Our objects may compare equal even if they have come from different |
454 | 132 | stores. | 113 | stores. |
455 | 133 | 114 | ||
463 | 134 | >>> auth_master_email = IMasterStore(EmailAddress).find( | 115 | >>> master_email = IMasterStore(EmailAddress).find( |
464 | 135 | ... EmailAddress, Person.name == 'janitor', | 116 | ... EmailAddress, Person.name == 'janitor', |
465 | 136 | ... EmailAddress.person==Person.id).one() | 117 | ... EmailAddress.person==Person.id).one() |
466 | 137 | >>> auth_slave_email = ISlaveStore(EmailAddress).find( | 118 | >>> slave_email = ISlaveStore(EmailAddress).find( |
467 | 138 | ... EmailAddress, Person.name == 'janitor', | 119 | ... EmailAddress, Person.name == 'janitor', |
468 | 139 | ... EmailAddress.person==Person.id).one() | 120 | ... EmailAddress.person==Person.id).one() |
469 | 140 | >>> auth_master_email is auth_slave_email | 121 | >>> master_email is slave_email |
470 | 141 | False | 122 | False |
472 | 142 | >>> auth_master_email == auth_slave_email | 123 | >>> master_email == slave_email |
473 | 143 | True | 124 | True |
475 | 144 | >>> auth_master_email != auth_slave_email | 125 | >>> master_email != slave_email |
476 | 145 | False | 126 | False |
477 | 146 | 127 | ||
478 | 147 | Comparison works for security wrapped objects too. | 128 | Comparison works for security wrapped objects too. |
479 | 148 | 129 | ||
480 | 149 | >>> wrapped_email = getUtility(IEmailAddressSet).getByEmail( | 130 | >>> wrapped_email = getUtility(IEmailAddressSet).getByEmail( |
483 | 150 | ... auth_master_email.email) | 131 | ... master_email.email) |
484 | 151 | >>> removeSecurityProxy(wrapped_email) is auth_master_email | 132 | >>> removeSecurityProxy(wrapped_email) is master_email |
485 | 152 | True | 133 | True |
487 | 153 | >>> wrapped_email is auth_master_email | 134 | >>> wrapped_email is master_email |
488 | 154 | False | 135 | False |
490 | 155 | >>> wrapped_email == auth_master_email | 136 | >>> wrapped_email == master_email |
491 | 156 | True | 137 | True |
493 | 157 | >>> wrapped_email != auth_master_email | 138 | >>> wrapped_email != master_email |
494 | 158 | False | 139 | False |
495 | 159 | 140 | ||
496 | 160 | Objects not yet flushed to the database also compare equal. | 141 | Objects not yet flushed to the database also compare equal. |
497 | @@ -176,10 +157,9 @@ | |||
498 | 176 | 157 | ||
499 | 177 | Objects differing by class never compare equal. | 158 | Objects differing by class never compare equal. |
500 | 178 | 159 | ||
504 | 179 | >>> account_one = IMasterStore(Account).get(Account, 1) | 160 | >>> email_one = IMasterStore(EmailAddress).get(EmailAddress, 1) |
505 | 180 | >>> person_one = IMasterStore(Account).get(Person, 1) | 161 | >>> person_one = IMasterStore(Person).get(Person, 1) |
506 | 181 | >>> account_one == person_one | 162 | >>> email_one == person_one |
507 | 182 | False | 163 | False |
509 | 183 | >>> account_one != person_one | 164 | >>> email_one != person_one |
510 | 184 | True | 165 | True |
511 | 185 | |||
512 | 186 | 166 | ||
513 | === modified file 'lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt' | |||
514 | --- lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt 2010-01-13 13:50:39 +0000 | |||
515 | +++ lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt 2010-04-05 20:44:34 +0000 | |||
516 | @@ -236,8 +236,6 @@ | |||
517 | 236 | ... [database] | 236 | ... [database] |
518 | 237 | ... rw_main_master: dbname=nonexistant | 237 | ... rw_main_master: dbname=nonexistant |
519 | 238 | ... rw_main_slave: dbname=nonexistant | 238 | ... rw_main_slave: dbname=nonexistant |
520 | 239 | ... auth_master: dbname=nonexistant | ||
521 | 240 | ... auth_slave: dbname=nonexistant | ||
522 | 241 | ... | 239 | ... |
523 | 242 | ... [launchpad_session] | 240 | ... [launchpad_session] |
524 | 243 | ... dbname: nonexistant | 241 | ... dbname: nonexistant |
525 | 244 | 242 | ||
526 | === modified file 'lib/canonical/launchpad/scripts/garbo.py' | |||
527 | --- lib/canonical/launchpad/scripts/garbo.py 2010-03-26 14:33:46 +0000 | |||
528 | +++ lib/canonical/launchpad/scripts/garbo.py 2010-04-05 20:44:34 +0000 | |||
529 | @@ -29,7 +29,7 @@ | |||
530 | 29 | from canonical.launchpad.utilities.looptuner import ( | 29 | from canonical.launchpad.utilities.looptuner import ( |
531 | 30 | DBLoopTuner, TunableLoop) | 30 | DBLoopTuner, TunableLoop) |
532 | 31 | from canonical.launchpad.webapp.interfaces import ( | 31 | from canonical.launchpad.webapp.interfaces import ( |
534 | 32 | IStoreSelector, AUTH_STORE, MAIN_STORE, MASTER_FLAVOR) | 32 | IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
535 | 33 | from lp.bugs.interfaces.bug import IBugSet | 33 | from lp.bugs.interfaces.bug import IBugSet |
536 | 34 | from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource | 34 | from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource |
537 | 35 | from lp.bugs.model.bugnotification import BugNotification | 35 | from lp.bugs.model.bugnotification import BugNotification |
538 | @@ -121,19 +121,17 @@ | |||
539 | 121 | transaction.commit() | 121 | transaction.commit() |
540 | 122 | 122 | ||
541 | 123 | 123 | ||
543 | 124 | class OpenIDAssociationPruner(TunableLoop): | 124 | class OpenIDConsumerAssociationPruner(TunableLoop): |
544 | 125 | minimum_chunk_size = 3500 | 125 | minimum_chunk_size = 3500 |
545 | 126 | maximum_chunk_size = 50000 | 126 | maximum_chunk_size = 50000 |
546 | 127 | 127 | ||
549 | 128 | table_name = 'OpenIDAssociation' | 128 | table_name = 'OpenIDConsumerAssociation' |
548 | 129 | store_name = AUTH_STORE | ||
550 | 130 | 129 | ||
551 | 131 | _num_removed = None | 130 | _num_removed = None |
552 | 132 | 131 | ||
553 | 133 | def __init__(self, log, abort_time=None): | 132 | def __init__(self, log, abort_time=None): |
557 | 134 | super(OpenIDAssociationPruner, self).__init__(log, abort_time) | 133 | super(OpenIDConsumerAssociationPruner, self).__init__(log, abort_time) |
558 | 135 | self.store = getUtility(IStoreSelector).get( | 134 | self.store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
556 | 136 | self.store_name, MASTER_FLAVOR) | ||
559 | 137 | 135 | ||
560 | 138 | def __call__(self, chunksize): | 136 | def __call__(self, chunksize): |
561 | 139 | result = self.store.execute(""" | 137 | result = self.store.execute(""" |
562 | @@ -152,11 +150,6 @@ | |||
563 | 152 | return self._num_removed == 0 | 150 | return self._num_removed == 0 |
564 | 153 | 151 | ||
565 | 154 | 152 | ||
566 | 155 | class OpenIDConsumerAssociationPruner(OpenIDAssociationPruner): | ||
567 | 156 | table_name = 'OpenIDConsumerAssociation' | ||
568 | 157 | store_name = MAIN_STORE | ||
569 | 158 | |||
570 | 159 | |||
571 | 160 | class RevisionCachePruner(TunableLoop): | 153 | class RevisionCachePruner(TunableLoop): |
572 | 161 | """A tunable loop to remove old revisions from the cache.""" | 154 | """A tunable loop to remove old revisions from the cache.""" |
573 | 162 | 155 | ||
574 | @@ -869,7 +862,6 @@ | |||
575 | 869 | tunable_loops = [ | 862 | tunable_loops = [ |
576 | 870 | OAuthNoncePruner, | 863 | OAuthNoncePruner, |
577 | 871 | OpenIDConsumerNoncePruner, | 864 | OpenIDConsumerNoncePruner, |
578 | 872 | OpenIDAssociationPruner, | ||
579 | 873 | OpenIDConsumerAssociationPruner, | 865 | OpenIDConsumerAssociationPruner, |
580 | 874 | RevisionCachePruner, | 866 | RevisionCachePruner, |
581 | 875 | BugHeatUpdater, | 867 | BugHeatUpdater, |
582 | 876 | 868 | ||
583 | === modified file 'lib/canonical/launchpad/scripts/tests/test_garbo.py' | |||
584 | --- lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-01-22 06:03:19 +0000 | |||
585 | +++ lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-04-05 20:44:34 +0000 | |||
586 | @@ -29,11 +29,11 @@ | |||
587 | 29 | from lp.testing import TestCase, TestCaseWithFactory | 29 | from lp.testing import TestCase, TestCaseWithFactory |
588 | 30 | from canonical.launchpad.scripts.garbo import ( | 30 | from canonical.launchpad.scripts.garbo import ( |
589 | 31 | DailyDatabaseGarbageCollector, HourlyDatabaseGarbageCollector, | 31 | DailyDatabaseGarbageCollector, HourlyDatabaseGarbageCollector, |
591 | 32 | OpenIDAssociationPruner, OpenIDConsumerAssociationPruner) | 32 | OpenIDConsumerAssociationPruner) |
592 | 33 | from canonical.launchpad.scripts.tests import run_script | 33 | from canonical.launchpad.scripts.tests import run_script |
593 | 34 | from canonical.launchpad.scripts.logger import QuietFakeLogger | 34 | from canonical.launchpad.scripts.logger import QuietFakeLogger |
594 | 35 | from canonical.launchpad.webapp.interfaces import ( | 35 | from canonical.launchpad.webapp.interfaces import ( |
596 | 36 | IStoreSelector, MASTER_FLAVOR) | 36 | IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
597 | 37 | from canonical.testing.layers import ( | 37 | from canonical.testing.layers import ( |
598 | 38 | DatabaseLayer, LaunchpadScriptLayer, LaunchpadZopelessLayer) | 38 | DatabaseLayer, LaunchpadScriptLayer, LaunchpadZopelessLayer) |
599 | 39 | from lp.bugs.model.bugnotification import ( | 39 | from lp.bugs.model.bugnotification import ( |
600 | @@ -219,12 +219,12 @@ | |||
601 | 219 | Min(CodeImportResult.date_created)).one().replace(tzinfo=UTC) | 219 | Min(CodeImportResult.date_created)).one().replace(tzinfo=UTC) |
602 | 220 | >= now - timedelta(days=30)) | 220 | >= now - timedelta(days=30)) |
603 | 221 | 221 | ||
606 | 222 | def test_OpenIDAssociationPruner(self, pruner=OpenIDAssociationPruner): | 222 | def test_OpenIDConsumerAssociationPruner(self): |
607 | 223 | store_name = pruner.store_name | 223 | pruner = OpenIDConsumerAssociationPruner |
608 | 224 | table_name = pruner.table_name | 224 | table_name = pruner.table_name |
609 | 225 | LaunchpadZopelessLayer.switchDbUser('testadmin') | 225 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
610 | 226 | store_selector = getUtility(IStoreSelector) | 226 | store_selector = getUtility(IStoreSelector) |
612 | 227 | store = store_selector.get(store_name, MASTER_FLAVOR) | 227 | store = store_selector.get(MAIN_STORE, MASTER_FLAVOR) |
613 | 228 | now = time.time() | 228 | now = time.time() |
614 | 229 | # Create some associations in the past with lifetimes | 229 | # Create some associations in the past with lifetimes |
615 | 230 | for delta in range(0, 20): | 230 | for delta in range(0, 20): |
616 | @@ -247,7 +247,7 @@ | |||
617 | 247 | self.runHourly() | 247 | self.runHourly() |
618 | 248 | 248 | ||
619 | 249 | LaunchpadZopelessLayer.switchDbUser('testadmin') | 249 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
621 | 250 | store = store_selector.get(store_name, MASTER_FLAVOR) | 250 | store = store_selector.get(MAIN_STORE, MASTER_FLAVOR) |
622 | 251 | # Confirm all the rows we know should have been expired have | 251 | # Confirm all the rows we know should have been expired have |
623 | 252 | # been expired. These are the ones that would be expired using | 252 | # been expired. These are the ones that would be expired using |
624 | 253 | # the test start time as 'now'. | 253 | # the test start time as 'now'. |
625 | @@ -263,9 +263,6 @@ | |||
626 | 263 | "SELECT COUNT(*) FROM %s" % table_name).get_one()[0] | 263 | "SELECT COUNT(*) FROM %s" % table_name).get_one()[0] |
627 | 264 | self.failUnless(num_unexpired > 0) | 264 | self.failUnless(num_unexpired > 0) |
628 | 265 | 265 | ||
629 | 266 | def test_OpenIDConsumerAssociationPruner(self): | ||
630 | 267 | self.test_OpenIDAssociationPruner(OpenIDConsumerAssociationPruner) | ||
631 | 268 | |||
632 | 269 | def test_RevisionAuthorEmailLinker(self): | 266 | def test_RevisionAuthorEmailLinker(self): |
633 | 270 | LaunchpadZopelessLayer.switchDbUser('testadmin') | 267 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
634 | 271 | rev1 = self.factory.makeRevision('Author 1 <author-1@Example.Org>') | 268 | rev1 = self.factory.makeRevision('Author 1 <author-1@Example.Org>') |
635 | @@ -373,6 +370,7 @@ | |||
636 | 373 | 370 | ||
637 | 374 | # If we remove the email address that was subscribed, the | 371 | # If we remove the email address that was subscribed, the |
638 | 375 | # garbage collector removes the subscription. | 372 | # garbage collector removes the subscription. |
639 | 373 | LaunchpadZopelessLayer.switchDbUser('testadmin') | ||
640 | 376 | Store.of(email).remove(email) | 374 | Store.of(email).remove(email) |
641 | 377 | transaction.commit() | 375 | transaction.commit() |
642 | 378 | self.runDaily() | 376 | self.runDaily() |
643 | 379 | 377 | ||
644 | === modified file 'lib/canonical/launchpad/webapp/adapter.py' | |||
645 | --- lib/canonical/launchpad/webapp/adapter.py 2010-03-25 11:58:42 +0000 | |||
646 | +++ lib/canonical/launchpad/webapp/adapter.py 2010-04-05 20:44:34 +0000 | |||
647 | @@ -40,14 +40,13 @@ | |||
648 | 40 | from canonical.launchpad.readonly import is_read_only | 40 | from canonical.launchpad.readonly import is_read_only |
649 | 41 | from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy | 41 | from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy |
650 | 42 | from canonical.launchpad.webapp.interfaces import ( | 42 | from canonical.launchpad.webapp.interfaces import ( |
653 | 43 | AUTH_STORE, DEFAULT_FLAVOR, IStoreSelector, | 43 | DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE, MASTER_FLAVOR, |
654 | 44 | MAIN_STORE, MASTER_FLAVOR, ReadOnlyModeViolation, SLAVE_FLAVOR) | 44 | ReadOnlyModeViolation, SLAVE_FLAVOR) |
655 | 45 | from canonical.launchpad.webapp.opstats import OpStats | 45 | from canonical.launchpad.webapp.opstats import OpStats |
656 | 46 | from canonical.lazr.utils import safe_hasattr | 46 | from canonical.lazr.utils import safe_hasattr |
657 | 47 | 47 | ||
658 | 48 | 48 | ||
659 | 49 | __all__ = [ | 49 | __all__ = [ |
660 | 50 | 'DisconnectionError', | ||
661 | 51 | 'RequestExpired', | 50 | 'RequestExpired', |
662 | 52 | 'set_request_started', | 51 | 'set_request_started', |
663 | 53 | 'clear_request_started', | 52 | 'clear_request_started', |
664 | @@ -327,7 +326,7 @@ | |||
665 | 327 | 326 | ||
666 | 328 | def __init__(self, uri): | 327 | def __init__(self, uri): |
667 | 329 | # The uri is just a property name in the config, such as main_master | 328 | # The uri is just a property name in the config, such as main_master |
669 | 330 | # or auth_slave. | 329 | # or main_slave. |
670 | 331 | # We don't invoke the superclass constructor as it has a very limited | 330 | # We don't invoke the superclass constructor as it has a very limited |
671 | 332 | # opinion on what uri is. | 331 | # opinion on what uri is. |
672 | 333 | # pylint: disable-msg=W0231 | 332 | # pylint: disable-msg=W0231 |
673 | @@ -356,7 +355,7 @@ | |||
674 | 356 | 'Connection uri %s does not match section-realm-flavor format' | 355 | 'Connection uri %s does not match section-realm-flavor format' |
675 | 357 | % repr(self._uri.database)) | 356 | % repr(self._uri.database)) |
676 | 358 | 357 | ||
678 | 359 | assert realm in ('main', 'auth'), 'Unknown realm %s' % realm | 358 | assert realm == 'main', 'Unknown realm %s' % realm |
679 | 360 | assert flavor in ('master', 'slave'), 'Unknown flavor %s' % flavor | 359 | assert flavor in ('master', 'slave'), 'Unknown flavor %s' % flavor |
680 | 361 | 360 | ||
681 | 362 | my_dbconfig = DatabaseConfig() | 361 | my_dbconfig = DatabaseConfig() |
682 | @@ -572,14 +571,6 @@ | |||
683 | 572 | return db_policy.getStore(name, flavor) | 571 | return db_policy.getStore(name, flavor) |
684 | 573 | 572 | ||
685 | 574 | 573 | ||
686 | 575 | # There are not many tables outside of the main replication set, so we | ||
687 | 576 | # can just maintain a hardcoded list of what isn't in there for now. | ||
688 | 577 | _auth_store_tables = frozenset([ | ||
689 | 578 | 'Account', 'AccountPassword', 'AuthToken', 'EmailAddress', | ||
690 | 579 | 'OpenIDAssociation', 'OpenIDAuthorization', 'OpenIDNonce', | ||
691 | 580 | 'OpenIDRPSummary']) | ||
692 | 581 | |||
693 | 582 | |||
694 | 583 | # We want to be able to adapt a Storm class to an IStore, IMasterStore or | 574 | # We want to be able to adapt a Storm class to an IStore, IMasterStore or |
695 | 584 | # ISlaveStore. Unfortunately, the component architecture provides no | 575 | # ISlaveStore. Unfortunately, the component architecture provides no |
696 | 585 | # way for us to declare that a class, and all its subclasses, provides | 576 | # way for us to declare that a class, and all its subclasses, provides |
697 | @@ -588,9 +579,7 @@ | |||
698 | 588 | def get_store(storm_class, flavor=DEFAULT_FLAVOR): | 579 | def get_store(storm_class, flavor=DEFAULT_FLAVOR): |
699 | 589 | """Return a flavored Store for the given database class.""" | 580 | """Return a flavored Store for the given database class.""" |
700 | 590 | table = getattr(removeSecurityProxy(storm_class), '__storm_table__', None) | 581 | table = getattr(removeSecurityProxy(storm_class), '__storm_table__', None) |
704 | 591 | if table in _auth_store_tables: | 582 | if table is not None: |
702 | 592 | return getUtility(IStoreSelector).get(AUTH_STORE, flavor) | ||
703 | 593 | elif table is not None: | ||
705 | 594 | return getUtility(IStoreSelector).get(MAIN_STORE, flavor) | 583 | return getUtility(IStoreSelector).get(MAIN_STORE, flavor) |
706 | 595 | else: | 584 | else: |
707 | 596 | return None | 585 | return None |
708 | 597 | 586 | ||
709 | === modified file 'lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt' | |||
710 | --- lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2009-06-11 01:28:55 +0000 | |||
711 | +++ lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2010-04-05 20:44:34 +0000 | |||
712 | @@ -10,8 +10,7 @@ | |||
713 | 10 | 10 | ||
714 | 11 | >>> from lp.registry.model.person import Person | 11 | >>> from lp.registry.model.person import Person |
715 | 12 | >>> from canonical.launchpad.webapp.interfaces import ( | 12 | >>> from canonical.launchpad.webapp.interfaces import ( |
718 | 13 | ... IStoreSelector, AUTH_STORE, MAIN_STORE, | 13 | ... IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR) |
717 | 14 | ... MASTER_FLAVOR, SLAVE_FLAVOR) | ||
719 | 15 | >>> import transaction | 14 | >>> import transaction |
720 | 16 | >>> from zope.component import getUtility | 15 | >>> from zope.component import getUtility |
721 | 17 | 16 | ||
722 | @@ -47,25 +46,3 @@ | |||
723 | 47 | >>> main_master.find(Person, name='janitor').one().displayname | 46 | >>> main_master.find(Person, name='janitor').one().displayname |
724 | 48 | u'BenD' | 47 | u'BenD' |
725 | 49 | >>> transaction.abort() | 48 | >>> transaction.abort() |
726 | 50 | |||
727 | 51 | |||
728 | 52 | A MASTER_FLAVOR Store does not allow writes to tables outside of that | ||
729 | 53 | Store's replication set. | ||
730 | 54 | |||
731 | 55 | >>> t = transaction.begin() | ||
732 | 56 | >>> person = main_master.find(Person, name='no-priv').one() | ||
733 | 57 | >>> account = person.account | ||
734 | 58 | >>> account.displayname = "Ben Dover" | ||
735 | 59 | >>> main_master.flush() | ||
736 | 60 | Traceback (most recent call last): | ||
737 | 61 | ... | ||
738 | 62 | ProgrammingError: permission denied for relation account | ||
739 | 63 | >>> transaction.abort() | ||
740 | 64 | |||
741 | 65 | >>> t = transaction.begin() | ||
742 | 66 | >>> auth_master = getUtility(IStoreSelector).get(AUTH_STORE, MASTER_FLAVOR) | ||
743 | 67 | >>> person = auth_master.find(Person, name='no-priv').one() | ||
744 | 68 | >>> account = person.account | ||
745 | 69 | >>> account.displayname = "Ben Dover" | ||
746 | 70 | >>> auth_master.flush() | ||
747 | 71 | >>> transaction.abort() | ||
748 | 72 | 49 | ||
749 | === modified file 'lib/canonical/launchpad/webapp/interfaces.py' | |||
750 | --- lib/canonical/launchpad/webapp/interfaces.py 2010-03-26 22:57:38 +0000 | |||
751 | +++ lib/canonical/launchpad/webapp/interfaces.py 2010-04-05 20:44:34 +0000 | |||
752 | @@ -745,9 +745,7 @@ | |||
753 | 745 | # | 745 | # |
754 | 746 | 746 | ||
755 | 747 | MAIN_STORE = 'main' # The main database. | 747 | MAIN_STORE = 'main' # The main database. |
759 | 748 | AUTH_STORE = 'auth' # The authentication database. | 748 | ALL_STORES = frozenset([MAIN_STORE]) |
757 | 749 | |||
758 | 750 | ALL_STORES = frozenset([MAIN_STORE, AUTH_STORE]) | ||
760 | 751 | 749 | ||
761 | 752 | DEFAULT_FLAVOR = 'default' # Default flavor for current state. | 750 | DEFAULT_FLAVOR = 'default' # Default flavor for current state. |
762 | 753 | MASTER_FLAVOR = 'master' # The master database. | 751 | MASTER_FLAVOR = 'master' # The master database. |
763 | 754 | 752 | ||
764 | === modified file 'lib/canonical/launchpad/webapp/tests/test_dbpolicy.py' | |||
765 | --- lib/canonical/launchpad/webapp/tests/test_dbpolicy.py 2010-02-24 23:18:40 +0000 | |||
766 | +++ lib/canonical/launchpad/webapp/tests/test_dbpolicy.py 2010-04-05 20:44:34 +0000 | |||
767 | @@ -25,7 +25,7 @@ | |||
768 | 25 | ReadOnlyLaunchpadDatabasePolicy, SlaveDatabasePolicy, | 25 | ReadOnlyLaunchpadDatabasePolicy, SlaveDatabasePolicy, |
769 | 26 | SlaveOnlyDatabasePolicy) | 26 | SlaveOnlyDatabasePolicy) |
770 | 27 | from canonical.launchpad.webapp.interfaces import ( | 27 | from canonical.launchpad.webapp.interfaces import ( |
772 | 28 | ALL_STORES, AUTH_STORE, DEFAULT_FLAVOR, DisallowedStore, IDatabasePolicy, | 28 | ALL_STORES, DEFAULT_FLAVOR, DisallowedStore, IDatabasePolicy, |
773 | 29 | IStoreSelector, MAIN_STORE, MASTER_FLAVOR, ReadOnlyModeDisallowedStore, | 29 | IStoreSelector, MAIN_STORE, MASTER_FLAVOR, ReadOnlyModeDisallowedStore, |
774 | 30 | SLAVE_FLAVOR) | 30 | SLAVE_FLAVOR) |
775 | 31 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest | 31 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
776 | @@ -47,9 +47,6 @@ | |||
777 | 47 | main_store = store_selector.get(MAIN_STORE, DEFAULT_FLAVOR) | 47 | main_store = store_selector.get(MAIN_STORE, DEFAULT_FLAVOR) |
778 | 48 | self.failUnlessEqual(self.getDBUser(main_store), 'launchpad_main') | 48 | self.failUnlessEqual(self.getDBUser(main_store), 'launchpad_main') |
779 | 49 | 49 | ||
780 | 50 | auth_store = store_selector.get(AUTH_STORE, DEFAULT_FLAVOR) | ||
781 | 51 | self.failUnlessEqual(self.getDBUser(auth_store), 'launchpad_auth') | ||
782 | 52 | |||
783 | 53 | def getDBUser(self, store): | 50 | def getDBUser(self, store): |
784 | 54 | return store.execute( | 51 | return store.execute( |
785 | 55 | 'SHOW session_authorization').get_one()[0] | 52 | 'SHOW session_authorization').get_one()[0] |
786 | 56 | 53 | ||
787 | === removed symlink 'lib/canonical/signon' | |||
788 | === target was u'../../sourcecode/canonical-identity-provider' | |||
789 | === modified file 'lib/lp/bugs/doc/checkwatches.txt' | |||
790 | --- lib/lp/bugs/doc/checkwatches.txt 2010-03-26 15:24:59 +0000 | |||
791 | +++ lib/lp/bugs/doc/checkwatches.txt 2010-04-05 20:44:34 +0000 | |||
792 | @@ -337,9 +337,13 @@ | |||
793 | 337 | If a bug tracker doesn't have any watches to update, forceUpdateAll() | 337 | If a bug tracker doesn't have any watches to update, forceUpdateAll() |
794 | 338 | will ignore it. | 338 | will ignore it. |
795 | 339 | 339 | ||
796 | 340 | >>> transaction.commit() | ||
797 | 341 | >>> LaunchpadZopelessLayer.switchDbUser('launchpad') | ||
798 | 340 | >>> login('test@canonical.com') | 342 | >>> login('test@canonical.com') |
799 | 341 | >>> empty_tracker = factory.makeBugTracker( | 343 | >>> empty_tracker = factory.makeBugTracker( |
800 | 342 | ... 'http://example.com', BugTrackerType.ROUNDUP) | 344 | ... 'http://example.com', BugTrackerType.ROUNDUP) |
801 | 345 | >>> transaction.commit() | ||
802 | 346 | >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser) | ||
803 | 343 | >>> empty_tracker_name = empty_tracker.name | 347 | >>> empty_tracker_name = empty_tracker.name |
804 | 344 | >>> update_all(empty_tracker_name) | 348 | >>> update_all(empty_tracker_name) |
805 | 345 | INFO Bug tracker 'auto-example.com' doesn't have any watches. Ignoring. | 349 | INFO Bug tracker 'auto-example.com' doesn't have any watches. Ignoring. |
806 | 346 | 350 | ||
807 | === modified file 'lib/lp/bugs/doc/externalbugtracker-comment-imports.txt' | |||
808 | --- lib/lp/bugs/doc/externalbugtracker-comment-imports.txt 2010-03-25 14:28:33 +0000 | |||
809 | +++ lib/lp/bugs/doc/externalbugtracker-comment-imports.txt 2010-04-05 20:44:34 +0000 | |||
810 | @@ -373,8 +373,10 @@ | |||
811 | 373 | since they aren't a valid Launchpad user, having been created during the | 373 | since they aren't a valid Launchpad user, having been created during the |
812 | 374 | import process. | 374 | import process. |
813 | 375 | 375 | ||
815 | 376 | We'll add a listener to check for Karma events. | 376 | We'll create a bug watch and add a listener to check for Karma events. |
816 | 377 | 377 | ||
817 | 378 | >>> LaunchpadZopelessLayer.switchDbUser('launchpad') | ||
818 | 379 | >>> bug_watch = factory.makeBugWatch('123456') | ||
819 | 378 | >>> from lp.registry.tests.karma import KarmaAssignedEventListener | 380 | >>> from lp.registry.tests.karma import KarmaAssignedEventListener |
820 | 379 | >>> karma_helper = KarmaAssignedEventListener() | 381 | >>> karma_helper = KarmaAssignedEventListener() |
821 | 380 | >>> karma_helper.register_listener() | 382 | >>> karma_helper.register_listener() |
822 | @@ -382,8 +384,6 @@ | |||
823 | 382 | Importing a comment with a CVE reference will produce a CVE link in | 384 | Importing a comment with a CVE reference will produce a CVE link in |
824 | 383 | Launchpad but will result in no Karma records being created. | 385 | Launchpad but will result in no Karma records being created. |
825 | 384 | 386 | ||
826 | 385 | >>> LaunchpadZopelessLayer.switchDbUser('launchpad') | ||
827 | 386 | >>> bug_watch = factory.makeBugWatch('123456') | ||
828 | 387 | >>> transaction.commit() | 387 | >>> transaction.commit() |
829 | 388 | >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser) | 388 | >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser) |
830 | 389 | >>> external_bugtracker.remote_comments = { | 389 | >>> external_bugtracker.remote_comments = { |
831 | 390 | 390 | ||
832 | === modified file 'lib/lp/bugs/interfaces/bug.py' | |||
833 | --- lib/lp/bugs/interfaces/bug.py 2010-03-12 06:24:59 +0000 | |||
834 | +++ lib/lp/bugs/interfaces/bug.py 2010-04-05 20:44:34 +0000 | |||
835 | @@ -288,9 +288,9 @@ | |||
836 | 288 | number_of_duplicates = exported( | 288 | number_of_duplicates = exported( |
837 | 289 | Int(title=_('The number of bugs marked as duplicates of this bug'), | 289 | Int(title=_('The number of bugs marked as duplicates of this bug'), |
838 | 290 | required=True, readonly=True)) | 290 | required=True, readonly=True)) |
842 | 291 | message_count = Int( | 291 | message_count = exported( |
843 | 292 | title=_('The number of comments on this bug'), | 292 | Int(title=_('The number of comments on this bug'), |
844 | 293 | required=True, readonly=True) | 293 | required=True, readonly=True)) |
845 | 294 | users_affected_count = exported( | 294 | users_affected_count = exported( |
846 | 295 | Int(title=_('The number of users affected by this bug ' | 295 | Int(title=_('The number of users affected by this bug ' |
847 | 296 | '(not including duplicates)'), | 296 | '(not including duplicates)'), |
848 | 297 | 297 | ||
849 | === modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt' | |||
850 | --- lib/lp/bugs/stories/webservice/xx-bug.txt 2010-03-24 21:59:44 +0000 | |||
851 | +++ lib/lp/bugs/stories/webservice/xx-bug.txt 2010-04-05 20:44:34 +0000 | |||
852 | @@ -34,6 +34,7 @@ | |||
853 | 34 | id: 11 | 34 | id: 11 |
854 | 35 | latest_patch_uploaded: None | 35 | latest_patch_uploaded: None |
855 | 36 | linked_branches_collection_link: u'http://.../bugs/11/linked_branches' | 36 | linked_branches_collection_link: u'http://.../bugs/11/linked_branches' |
856 | 37 | message_count: 7 | ||
857 | 37 | messages_collection_link: u'http://.../bugs/11/messages' | 38 | messages_collection_link: u'http://.../bugs/11/messages' |
858 | 38 | name: None | 39 | name: None |
859 | 39 | number_of_duplicates: 0 | 40 | number_of_duplicates: 0 |
860 | 40 | 41 | ||
861 | === modified file 'lib/lp/code/doc/branch-karma.txt' | |||
862 | --- lib/lp/code/doc/branch-karma.txt 2010-02-16 20:36:48 +0000 | |||
863 | +++ lib/lp/code/doc/branch-karma.txt 2010-04-05 20:44:34 +0000 | |||
864 | @@ -49,6 +49,7 @@ | |||
865 | 49 | You get karma for linking a bug to a branch. | 49 | You get karma for linking a bug to a branch. |
866 | 50 | 50 | ||
867 | 51 | >>> bug = factory.makeBug(product=fooix) | 51 | >>> bug = factory.makeBug(product=fooix) |
868 | 52 | Karma added: action=bugcreated, product=fooix, person=person-name11 | ||
869 | 52 | >>> branch_link = bug.linkBranch(branch, eric) | 53 | >>> branch_link = bug.linkBranch(branch, eric) |
870 | 53 | Karma added: action=bugbranchcreated, product=fooix, person=eric | 54 | Karma added: action=bugbranchcreated, product=fooix, person=eric |
871 | 54 | 55 | ||
872 | 55 | 56 | ||
873 | === modified file 'lib/lp/registry/configure.zcml' | |||
874 | --- lib/lp/registry/configure.zcml 2010-03-19 11:33:44 +0000 | |||
875 | +++ lib/lp/registry/configure.zcml 2010-04-05 20:44:34 +0000 | |||
876 | @@ -1644,18 +1644,60 @@ | |||
877 | 1644 | <!-- DistributionMirror --> | 1644 | <!-- DistributionMirror --> |
878 | 1645 | <class class="lp.registry.model.distributionmirror.DistributionMirror"> | 1645 | <class class="lp.registry.model.distributionmirror.DistributionMirror"> |
879 | 1646 | <allow | 1646 | <allow |
881 | 1647 | interface="lp.registry.interfaces.distributionmirror.IDistributionMirrorPublic" /> | 1647 | attributes=" |
882 | 1648 | id | ||
883 | 1649 | name | ||
884 | 1650 | displayname | ||
885 | 1651 | description | ||
886 | 1652 | distribution | ||
887 | 1653 | http_base_url | ||
888 | 1654 | ftp_base_url | ||
889 | 1655 | rsync_base_url | ||
890 | 1656 | enabled | ||
891 | 1657 | speed | ||
892 | 1658 | status | ||
893 | 1659 | country | ||
894 | 1660 | content | ||
895 | 1661 | owner | ||
896 | 1662 | title | ||
897 | 1663 | cdimage_series | ||
898 | 1664 | source_series | ||
899 | 1665 | arch_series | ||
900 | 1666 | last_probe_record | ||
901 | 1667 | all_probe_records | ||
902 | 1668 | has_ftp_or_rsync_base_url | ||
903 | 1669 | base_url | ||
904 | 1670 | date_created | ||
905 | 1671 | country_dns_mirror | ||
906 | 1672 | mirrorMustHaveHTTPOrFTPURL | ||
907 | 1673 | getSummarizedMirroredSourceSeries | ||
908 | 1674 | getSummarizedMirroredArchSeries | ||
909 | 1675 | getOverallFreshness | ||
910 | 1676 | isOfficial | ||
911 | 1677 | shouldDisable | ||
912 | 1678 | disable | ||
913 | 1679 | newProbeRecord | ||
914 | 1680 | deleteMirrorDistroArchSeries | ||
915 | 1681 | ensureMirrorDistroArchSeries | ||
916 | 1682 | ensureMirrorDistroSeriesSource | ||
917 | 1683 | deleteMirrorDistroSeriesSource | ||
918 | 1684 | ensureMirrorCDImageSeries | ||
919 | 1685 | deleteMirrorCDImageSeries | ||
920 | 1686 | deleteAllMirrorCDImageSeries | ||
921 | 1687 | getExpectedPackagesPaths | ||
922 | 1688 | getExpectedSourcesPaths | ||
923 | 1689 | canTransitionToCountryMirror" /> | ||
924 | 1648 | <require | 1690 | <require |
925 | 1649 | permission="launchpad.Edit" | 1691 | permission="launchpad.Edit" |
926 | 1650 | interface="lp.registry.interfaces.distributionmirror.IDistributionMirrorEditRestricted" | ||
927 | 1651 | set_attributes="name displayname description whiteboard | 1692 | set_attributes="name displayname description whiteboard |
928 | 1652 | http_base_url ftp_base_url rsync_base_url enabled | 1693 | http_base_url ftp_base_url rsync_base_url enabled |
930 | 1653 | speed country content official_candidate owner" /> | 1694 | speed country content official_candidate owner" |
931 | 1695 | attributes="official_candidate whiteboard" /> | ||
932 | 1654 | <require | 1696 | <require |
933 | 1655 | permission="launchpad.Admin" | 1697 | permission="launchpad.Admin" |
934 | 1656 | interface="lp.registry.interfaces.distributionmirror.IDistributionMirrorAdminRestricted" | ||
935 | 1657 | set_attributes="status reviewer date_reviewed" | 1698 | set_attributes="status reviewer date_reviewed" |
937 | 1658 | attributes="destroySelf" /> | 1699 | attributes="reviewer date_reviewed destroySelf |
938 | 1700 | transitionToCountryMirror" /> | ||
939 | 1659 | </class> | 1701 | </class> |
940 | 1660 | 1702 | ||
941 | 1661 | <adapter | 1703 | <adapter |
942 | 1662 | 1704 | ||
943 | === modified file 'lib/lp/registry/doc/distribution-mirror.txt' | |||
944 | --- lib/lp/registry/doc/distribution-mirror.txt 2009-12-09 10:03:20 +0000 | |||
945 | +++ lib/lp/registry/doc/distribution-mirror.txt 2010-04-05 20:44:34 +0000 | |||
946 | @@ -8,7 +8,7 @@ | |||
947 | 8 | >>> from canonical.launchpad.interfaces import ( | 8 | >>> from canonical.launchpad.interfaces import ( |
948 | 9 | ... ICountrySet, IDistributionSet, IDistributionMirrorSet, | 9 | ... ICountrySet, IDistributionSet, IDistributionMirrorSet, |
949 | 10 | ... IDistroArchSeriesSet, IDistroSeriesSet, ILibraryFileAliasSet, | 10 | ... IDistroArchSeriesSet, IDistroSeriesSet, ILibraryFileAliasSet, |
951 | 11 | ... IPersonSet, MirrorContent, MirrorSpeed) | 11 | ... IPersonSet, MirrorContent, MirrorSpeed, MirrorStatus) |
952 | 12 | >>> from lp.registry.interfaces.pocket import PackagePublishingPocket | 12 | >>> from lp.registry.interfaces.pocket import PackagePublishingPocket |
953 | 13 | >>> mirrorset = getUtility(IDistributionMirrorSet) | 13 | >>> mirrorset = getUtility(IDistributionMirrorSet) |
954 | 14 | >>> distroset = getUtility(IDistributionSet) | 14 | >>> distroset = getUtility(IDistributionSet) |
955 | @@ -814,3 +814,121 @@ | |||
956 | 814 | >>> mirrorset.getByName('invalid-mirror') is None | 814 | >>> mirrorset.getByName('invalid-mirror') is None |
957 | 815 | True | 815 | True |
958 | 816 | 816 | ||
959 | 817 | Country DNS mirrors | ||
960 | 818 | ------------------- | ||
961 | 819 | |||
962 | 820 | Country DNS mirrors are mirrors which have been assigned | ||
963 | 821 | $CC.archive.ubuntu.com or $CC.releases.ubuntu.com. These assignments are | ||
964 | 822 | tracked in Launchpad. | ||
965 | 823 | |||
966 | 824 | >>> login('admin@canonical.com') | ||
967 | 825 | >>> ubuntu_distro = getUtility(IDistributionSet).getByName('ubuntu') | ||
968 | 826 | >>> de_archive_mirror = factory.makeMirror(ubuntu_distro, | ||
969 | 827 | ... "Technische Universitaet Dresden", country=82, | ||
970 | 828 | ... http_url="http://ubuntu.mirror.tudos.de/ubuntu/", | ||
971 | 829 | ... official_candidate=True) | ||
972 | 830 | >>> davis_station_archive = factory.makeMirror(ubuntu_distro, | ||
973 | 831 | ... "Davis Station", country=9, | ||
974 | 832 | ... http_url="http://mirror.davis.antarctica.org/ubuntu", | ||
975 | 833 | ... official_candidate=True) | ||
976 | 834 | >>> de_archive_mirror.status = MirrorStatus.OFFICIAL | ||
977 | 835 | >>> de_archive_prober_log = factory.makeMirrorProbeRecord(de_archive_mirror) | ||
978 | 836 | >>> logout() | ||
979 | 837 | |||
980 | 838 | Normal users can access country_dns_mirror, can see if a mirror is eligible | ||
981 | 839 | for the status, however, they may not change it: | ||
982 | 840 | |||
983 | 841 | >>> login('test@canonical.com') | ||
984 | 842 | >>> de_archive_mirror.canTransitionToCountryMirror() | ||
985 | 843 | True | ||
986 | 844 | >>> de_archive_mirror.transitionToCountryMirror(True) | ||
987 | 845 | Traceback (most recent call last): | ||
988 | 846 | ... | ||
989 | 847 | Unauthorized: (<DistributionMirror at ...>, 'transitionToCountryMirror', | ||
990 | 848 | 'launchpad.Admin') | ||
991 | 849 | >>> logout() | ||
992 | 850 | |||
993 | 851 | Mirror listing administrators may change the status however: | ||
994 | 852 | |||
995 | 853 | >>> login('karl@canonical.com') | ||
996 | 854 | >>> de_archive_mirror.transitionToCountryMirror(True) | ||
997 | 855 | |||
998 | 856 | Mirrors which are already set as country mirrors can't be 'set' as such | ||
999 | 857 | again: | ||
1000 | 858 | |||
1001 | 859 | >>> de_archive_mirror.canTransitionToCountryMirror() | ||
1002 | 860 | False | ||
1003 | 861 | >>> de_archive_mirror.transitionToCountryMirror(True) | ||
1004 | 862 | >>> logout() | ||
1005 | 863 | |||
1006 | 864 | There cannot be multiple country mirrors of one type for one country: | ||
1007 | 865 | |||
1008 | 866 | >>> login('karl@canonical.com') | ||
1009 | 867 | >>> proberecord = factory.makeMirrorProbeRecord(davis_station_archive) | ||
1010 | 868 | |||
1011 | 869 | >>> print davis_station_archive.content.name | ||
1012 | 870 | ARCHIVE | ||
1013 | 871 | >>> print davis_station_archive.country_dns_mirror | ||
1014 | 872 | False | ||
1015 | 873 | >>> print davis_station_archive.country.name | ||
1016 | 874 | Antarctica | ||
1017 | 875 | |||
1018 | 876 | >>> archive_mirror2 = getUtility(IDistributionMirrorSet).getByName( | ||
1019 | 877 | ... 'archive-mirror2') | ||
1020 | 878 | >>> print archive_mirror2.content.name | ||
1021 | 879 | ARCHIVE | ||
1022 | 880 | >>> print archive_mirror2.country_dns_mirror | ||
1023 | 881 | False | ||
1024 | 882 | >>> print archive_mirror2.country.name | ||
1025 | 883 | Antarctica | ||
1026 | 884 | |||
1027 | 885 | >>> davis_station_archive.status = MirrorStatus.OFFICIAL | ||
1028 | 886 | |||
1029 | 887 | >>> davis_station_archive.transitionToCountryMirror(True) | ||
1030 | 888 | >>> archive_mirror2.transitionToCountryMirror(True) | ||
1031 | 889 | Traceback (most recent call last): | ||
1032 | 890 | ... | ||
1033 | 891 | CountryMirrorAlreadySet: Antarctica already has a country Archive mirror | ||
1034 | 892 | set. | ||
1035 | 893 | |||
1036 | 894 | Mirrors which have not been probed may not be marked as country mirrors: | ||
1037 | 895 | |||
1038 | 896 | >>> linux_au_mirror = factory.makeMirror(ubuntu_distro, | ||
1039 | 897 | ... "Linux.org.au", country=14, | ||
1040 | 898 | ... http_url="http://mirror.linux.org.au/ubuntu", | ||
1041 | 899 | ... official_candidate=True) | ||
1042 | 900 | >>> linux_au_mirror.status = MirrorStatus.OFFICIAL | ||
1043 | 901 | >>> linux_au_mirror.transitionToCountryMirror(True) | ||
1044 | 902 | Traceback (most recent call last): | ||
1045 | 903 | ... | ||
1046 | 904 | MirrorNotProbed: This mirror may not be set as a country mirror as it has | ||
1047 | 905 | not been probed. | ||
1048 | 906 | >>> logout() | ||
1049 | 907 | |||
1050 | 908 | Mirrors which are not official or do not have an HTTP URL may not be set as | ||
1051 | 909 | country mirrors: | ||
1052 | 910 | |||
1053 | 911 | >>> login('admin@canonical.com') | ||
1054 | 912 | >>> osuosl_mirror = factory.makeMirror(ubuntu_distro, | ||
1055 | 913 | ... "OSU Open Source Lab", country=226, | ||
1056 | 914 | ... ftp_url="ftp://ubuntu.osuosl.org/pub/ubuntu/", | ||
1057 | 915 | ... official_candidate=True) | ||
1058 | 916 | >>> osuosl_mirror.status = MirrorStatus.OFFICIAL | ||
1059 | 917 | >>> print osuosl_mirror.http_base_url | ||
1060 | 918 | None | ||
1061 | 919 | |||
1062 | 920 | >>> osuosl_mirror.canTransitionToCountryMirror() | ||
1063 | 921 | False | ||
1064 | 922 | |||
1065 | 923 | >>> osuosl_mirror.transitionToCountryMirror(None) | ||
1066 | 924 | Traceback (most recent call last): | ||
1067 | 925 | ... | ||
1068 | 926 | NoneError: None isn't acceptable as a value for | ||
1069 | 927 | DistributionMirror.country_dns_mirror | ||
1070 | 928 | |||
1071 | 929 | >>> osuosl_mirror.transitionToCountryMirror(True) | ||
1072 | 930 | Traceback (most recent call last): | ||
1073 | 931 | ... | ||
1074 | 932 | MirrorHasNoHTTPURL: This mirror may not be set as a country mirror as it | ||
1075 | 933 | does not have an HTTP URL set. | ||
1076 | 934 | >>> logout() | ||
1077 | 817 | 935 | ||
1078 | === modified file 'lib/lp/registry/interfaces/distribution.py' | |||
1079 | --- lib/lp/registry/interfaces/distribution.py 2010-03-24 21:59:58 +0000 | |||
1080 | +++ lib/lp/registry/interfaces/distribution.py 2010-04-05 20:44:34 +0000 | |||
1081 | @@ -25,7 +25,7 @@ | |||
1082 | 25 | 25 | ||
1083 | 26 | from lazr.restful.fields import CollectionField, Reference | 26 | from lazr.restful.fields import CollectionField, Reference |
1084 | 27 | from lazr.restful.declarations import ( | 27 | from lazr.restful.declarations import ( |
1086 | 28 | collection_default_content, export_as_webservice_collection, | 28 | collection_default_content, copy_field, export_as_webservice_collection, |
1087 | 29 | export_as_webservice_entry, export_operation_as, | 29 | export_as_webservice_entry, export_operation_as, |
1088 | 30 | export_read_operation, exported, operation_parameters, | 30 | export_read_operation, exported, operation_parameters, |
1089 | 31 | operation_returns_collection_of, operation_returns_entry, | 31 | operation_returns_collection_of, operation_returns_entry, |
1090 | @@ -321,6 +321,14 @@ | |||
1091 | 321 | if it's not found. | 321 | if it's not found. |
1092 | 322 | """ | 322 | """ |
1093 | 323 | 323 | ||
1094 | 324 | @operation_parameters( | ||
1095 | 325 | country=copy_field(IDistributionMirror['country'], required=True), | ||
1096 | 326 | mirror_type=copy_field(IDistributionMirror['content'], required=True)) | ||
1097 | 327 | @operation_returns_entry(IDistributionMirror) | ||
1098 | 328 | @export_read_operation() | ||
1099 | 329 | def getCountryMirror(country, mirror_type): | ||
1100 | 330 | """Return the country DNS mirror for a country and content type.""" | ||
1101 | 331 | |||
1102 | 324 | def newMirror(owner, speed, country, content, displayname=None, | 332 | def newMirror(owner, speed, country, content, displayname=None, |
1103 | 325 | description=None, http_base_url=None, | 333 | description=None, http_base_url=None, |
1104 | 326 | ftp_base_url=None, rsync_base_url=None, enabled=False, | 334 | ftp_base_url=None, rsync_base_url=None, enabled=False, |
1105 | 327 | 335 | ||
1106 | === modified file 'lib/lp/registry/interfaces/distributionmirror.py' | |||
1107 | --- lib/lp/registry/interfaces/distributionmirror.py 2010-02-22 15:50:06 +0000 | |||
1108 | +++ lib/lp/registry/interfaces/distributionmirror.py 2010-04-05 20:44:34 +0000 | |||
1109 | @@ -6,21 +6,24 @@ | |||
1110 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
1111 | 7 | 7 | ||
1112 | 8 | __all__ = [ | 8 | __all__ = [ |
1128 | 9 | 'IDistributionMirror', | 9 | 'CannotTransitionToCountryMirror', |
1129 | 10 | 'IDistributionMirrorAdminRestricted', | 10 | 'CountryMirrorAlreadySet', |
1130 | 11 | 'IDistributionMirrorEditRestricted', | 11 | 'IDistributionMirror', |
1131 | 12 | 'IDistributionMirrorPublic', | 12 | 'IMirrorDistroArchSeries', |
1132 | 13 | 'IMirrorDistroArchSeries', | 13 | 'IMirrorDistroSeriesSource', |
1133 | 14 | 'IMirrorDistroSeriesSource', | 14 | 'IMirrorProbeRecord', |
1134 | 15 | 'IMirrorProbeRecord', | 15 | 'IDistributionMirrorSet', |
1135 | 16 | 'IDistributionMirrorSet', | 16 | 'IMirrorCDImageDistroSeries', |
1136 | 17 | 'IMirrorCDImageDistroSeries', | 17 | 'PROBE_INTERVAL', |
1137 | 18 | 'PROBE_INTERVAL', | 18 | 'MirrorContent', |
1138 | 19 | 'UnableToFetchCDImageFileList', | 19 | 'MirrorFreshness', |
1139 | 20 | 'MirrorContent', | 20 | 'MirrorHasNoHTTPURL', |
1140 | 21 | 'MirrorFreshness', | 21 | 'MirrorNotOfficial', |
1141 | 22 | 'MirrorSpeed', | 22 | 'MirrorNotProbed', |
1142 | 23 | 'MirrorStatus'] | 23 | 'MirrorSpeed', |
1143 | 24 | 'MirrorStatus', | ||
1144 | 25 | 'UnableToFetchCDImageFileList', | ||
1145 | 26 | ] | ||
1146 | 24 | 27 | ||
1147 | 25 | from cgi import escape | 28 | from cgi import escape |
1148 | 26 | 29 | ||
1149 | @@ -31,8 +34,11 @@ | |||
1150 | 31 | from zope.component import getUtility | 34 | from zope.component import getUtility |
1151 | 32 | from lazr.enum import DBEnumeratedType, DBItem | 35 | from lazr.enum import DBEnumeratedType, DBItem |
1152 | 33 | from lazr.restful.declarations import ( | 36 | from lazr.restful.declarations import ( |
1154 | 34 | export_as_webservice_entry, export_read_operation, exported) | 37 | export_as_webservice_entry, export_read_operation, |
1155 | 38 | export_write_operation, exported, mutator_for, operation_parameters, | ||
1156 | 39 | webservice_error) | ||
1157 | 35 | from lazr.restful.fields import Reference, ReferenceChoice | 40 | from lazr.restful.fields import Reference, ReferenceChoice |
1158 | 41 | from lazr.restful.interface import copy_field | ||
1159 | 36 | 42 | ||
1160 | 37 | from canonical.launchpad import _ | 43 | from canonical.launchpad import _ |
1161 | 38 | from canonical.launchpad.fields import ( | 44 | from canonical.launchpad.fields import ( |
1162 | @@ -47,6 +53,43 @@ | |||
1163 | 47 | PROBE_INTERVAL = 23 | 53 | PROBE_INTERVAL = 23 |
1164 | 48 | 54 | ||
1165 | 49 | 55 | ||
1166 | 56 | class CannotTransitionToCountryMirror(Exception): | ||
1167 | 57 | """Root exception for transitions to country mirrors.""" | ||
1168 | 58 | webservice_error(400) | ||
1169 | 59 | |||
1170 | 60 | |||
1171 | 61 | class CountryMirrorAlreadySet(CannotTransitionToCountryMirror): | ||
1172 | 62 | """Distribution mirror cannot be set as a country mirror. | ||
1173 | 63 | |||
1174 | 64 | Raised when a user tries to change set a distribution mirror as a country | ||
1175 | 65 | mirror, however there is already one set for that country. | ||
1176 | 66 | """ | ||
1177 | 67 | |||
1178 | 68 | |||
1179 | 69 | class MirrorNotOfficial(CannotTransitionToCountryMirror): | ||
1180 | 70 | """Distribution mirror is not permitted to become a country mirror. | ||
1181 | 71 | |||
1182 | 72 | Raised when a user tries to change set a distribution mirror as a country | ||
1183 | 73 | mirror, however the mirror in question is not official. | ||
1184 | 74 | """ | ||
1185 | 75 | |||
1186 | 76 | |||
1187 | 77 | class MirrorHasNoHTTPURL(CannotTransitionToCountryMirror): | ||
1188 | 78 | """Distribution mirror has no HTTP URL. | ||
1189 | 79 | |||
1190 | 80 | Raised when a user tries to make an official mirror a country mirror, | ||
1191 | 81 | however the mirror has not HTTP URL set. | ||
1192 | 82 | """ | ||
1193 | 83 | |||
1194 | 84 | |||
1195 | 85 | class MirrorNotProbed(CannotTransitionToCountryMirror): | ||
1196 | 86 | """Distribution mirror has not been probed. | ||
1197 | 87 | |||
1198 | 88 | Raised when a user tries to set an official mirror as a country mirror, | ||
1199 | 89 | however the mirror has not been probed yet. | ||
1200 | 90 | """ | ||
1201 | 91 | |||
1202 | 92 | |||
1203 | 50 | class MirrorContent(DBEnumeratedType): | 93 | class MirrorContent(DBEnumeratedType): |
1204 | 51 | """The content that is mirrored.""" | 94 | """The content that is mirrored.""" |
1205 | 52 | 95 | ||
1206 | @@ -284,33 +327,10 @@ | |||
1207 | 284 | def getMirrorByURI(self, url): | 327 | def getMirrorByURI(self, url): |
1208 | 285 | return getUtility(IDistributionMirrorSet).getByRsyncUrl(url) | 328 | return getUtility(IDistributionMirrorSet).getByRsyncUrl(url) |
1209 | 286 | 329 | ||
1237 | 287 | class IDistributionMirrorAdminRestricted(Interface): | 330 | |
1238 | 288 | """IDistributionMirror properties requiring launchpad.Admin permission.""" | 331 | class IDistributionMirror(Interface): |
1239 | 289 | 332 | """A mirror of a given distribution.""" | |
1240 | 290 | reviewer = exported(PublicPersonChoice( | 333 | export_as_webservice_entry() |
1214 | 291 | title=_('Reviewer'), required=False, readonly=True, | ||
1215 | 292 | vocabulary='ValidPersonOrTeam', description=_( | ||
1216 | 293 | "The person who last reviewed this mirror."))) | ||
1217 | 294 | date_reviewed = exported(Datetime( | ||
1218 | 295 | title=_('Date reviewed'), required=False, readonly=True, | ||
1219 | 296 | description=_( | ||
1220 | 297 | "The date on which this mirror was last reviewed by a mirror admin."))) | ||
1221 | 298 | |||
1222 | 299 | |||
1223 | 300 | class IDistributionMirrorEditRestricted(Interface): | ||
1224 | 301 | """IDistributionMirror properties requiring launchpad.Edit permission.""" | ||
1225 | 302 | |||
1226 | 303 | official_candidate = exported(Bool( | ||
1227 | 304 | title=_('Apply to be an official mirror of this distribution'), | ||
1228 | 305 | required=False, readonly=False, default=True)) | ||
1229 | 306 | whiteboard = exported(Whiteboard( | ||
1230 | 307 | title=_('Whiteboard'), required=False, readonly=False, | ||
1231 | 308 | description=_("Notes on the current status of the mirror (only " | ||
1232 | 309 | "visible to admins and the mirror's registrant)."))) | ||
1233 | 310 | |||
1234 | 311 | |||
1235 | 312 | class IDistributionMirrorPublic(Interface): | ||
1236 | 313 | """Public IDistributionMirror properties.""" | ||
1241 | 314 | 334 | ||
1242 | 315 | id = Int(title=_('The unique id'), required=True, readonly=True) | 335 | id = Int(title=_('The unique id'), required=True, readonly=True) |
1243 | 316 | owner = exported(PublicPersonChoice( | 336 | owner = exported(PublicPersonChoice( |
1244 | @@ -386,6 +406,39 @@ | |||
1245 | 386 | date_created = exported(Datetime( | 406 | date_created = exported(Datetime( |
1246 | 387 | title=_('Date Created'), required=True, readonly=True, | 407 | title=_('Date Created'), required=True, readonly=True, |
1247 | 388 | description=_("The date on which this mirror was registered."))) | 408 | description=_("The date on which this mirror was registered."))) |
1248 | 409 | country_dns_mirror = exported(Bool( | ||
1249 | 410 | title=_('Country DNS Mirror'), | ||
1250 | 411 | description=_('Whether this is a country mirror in DNS.'), | ||
1251 | 412 | required=False, readonly=True, default=False)) | ||
1252 | 413 | |||
1253 | 414 | reviewer = exported(PublicPersonChoice( | ||
1254 | 415 | title=_('Reviewer'), required=False, readonly=True, | ||
1255 | 416 | vocabulary='ValidPersonOrTeam', description=_( | ||
1256 | 417 | "The person who last reviewed this mirror."))) | ||
1257 | 418 | date_reviewed = exported(Datetime( | ||
1258 | 419 | title=_('Date reviewed'), required=False, readonly=True, | ||
1259 | 420 | description=_( | ||
1260 | 421 | "The date on which this mirror was last reviewed by a mirror " | ||
1261 | 422 | "admin."))) | ||
1262 | 423 | |||
1263 | 424 | official_candidate = exported(Bool( | ||
1264 | 425 | title=_('Apply to be an official mirror of this distribution'), | ||
1265 | 426 | required=False, readonly=False, default=True)) | ||
1266 | 427 | whiteboard = exported(Whiteboard( | ||
1267 | 428 | title=_('Whiteboard'), required=False, readonly=False, | ||
1268 | 429 | description=_("Notes on the current status of the mirror (only " | ||
1269 | 430 | "visible to admins and the mirror's registrant)."))) | ||
1270 | 431 | |||
1271 | 432 | @export_read_operation() | ||
1272 | 433 | def canTransitionToCountryMirror(): | ||
1273 | 434 | """Verify if a mirror can be set as a country mirror or return | ||
1274 | 435 | False.""" | ||
1275 | 436 | |||
1276 | 437 | @mutator_for(country_dns_mirror) | ||
1277 | 438 | @operation_parameters(country_dns_mirror=copy_field(country_dns_mirror)) | ||
1278 | 439 | @export_write_operation() | ||
1279 | 440 | def transitionToCountryMirror(country_dns_mirror): | ||
1280 | 441 | """Method run on changing country_dns_mirror.""" | ||
1281 | 389 | 442 | ||
1282 | 390 | @invariant | 443 | @invariant |
1283 | 391 | def mirrorMustHaveHTTPOrFTPURL(mirror): | 444 | def mirrorMustHaveHTTPOrFTPURL(mirror): |
1284 | @@ -521,10 +574,6 @@ | |||
1285 | 521 | Sources.gz file refer to and the path to the file itself. | 574 | Sources.gz file refer to and the path to the file itself. |
1286 | 522 | """ | 575 | """ |
1287 | 523 | 576 | ||
1288 | 524 | class IDistributionMirror(IDistributionMirrorAdminRestricted, | ||
1289 | 525 | IDistributionMirrorEditRestricted, IDistributionMirrorPublic): | ||
1290 | 526 | """A mirror of a given distribution.""" | ||
1291 | 527 | export_as_webservice_entry() | ||
1292 | 528 | 577 | ||
1293 | 529 | 578 | ||
1294 | 530 | class UnableToFetchCDImageFileList(Exception): | 579 | class UnableToFetchCDImageFileList(Exception): |
1295 | 531 | 580 | ||
1296 | === modified file 'lib/lp/registry/model/distribution.py' | |||
1297 | --- lib/lp/registry/model/distribution.py 2010-03-24 02:53:42 +0000 | |||
1298 | +++ lib/lp/registry/model/distribution.py 2010-04-05 20:44:34 +0000 | |||
1299 | @@ -402,6 +402,17 @@ | |||
1300 | 402 | """See `IDistribution`.""" | 402 | """See `IDistribution`.""" |
1301 | 403 | return DistributionMirror.selectOneBy(distribution=self, name=name) | 403 | return DistributionMirror.selectOneBy(distribution=self, name=name) |
1302 | 404 | 404 | ||
1303 | 405 | def getCountryMirror(self, country, mirror_type): | ||
1304 | 406 | """See `IDistribution`.""" | ||
1305 | 407 | store = Store.of(self) | ||
1306 | 408 | results = store.find( | ||
1307 | 409 | DistributionMirror, | ||
1308 | 410 | DistributionMirror.distribution == self, | ||
1309 | 411 | DistributionMirror.country == country, | ||
1310 | 412 | DistributionMirror.content == mirror_type, | ||
1311 | 413 | DistributionMirror.country_dns_mirror == True) | ||
1312 | 414 | return results.one() | ||
1313 | 415 | |||
1314 | 405 | def newMirror(self, owner, speed, country, content, displayname=None, | 416 | def newMirror(self, owner, speed, country, content, displayname=None, |
1315 | 406 | description=None, http_base_url=None, | 417 | description=None, http_base_url=None, |
1316 | 407 | ftp_base_url=None, rsync_base_url=None, | 418 | ftp_base_url=None, rsync_base_url=None, |
1317 | 408 | 419 | ||
1318 | === modified file 'lib/lp/registry/model/distributionmirror.py' | |||
1319 | --- lib/lp/registry/model/distributionmirror.py 2010-03-23 20:42:23 +0000 | |||
1320 | +++ lib/lp/registry/model/distributionmirror.py 2010-04-05 20:44:34 +0000 | |||
1321 | @@ -45,9 +45,11 @@ | |||
1322 | 45 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | 45 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1323 | 46 | from lp.soyuz.interfaces.publishing import PackagePublishingStatus | 46 | from lp.soyuz.interfaces.publishing import PackagePublishingStatus |
1324 | 47 | from lp.registry.interfaces.distributionmirror import ( | 47 | from lp.registry.interfaces.distributionmirror import ( |
1325 | 48 | CannotTransitionToCountryMirror, CountryMirrorAlreadySet, | ||
1326 | 48 | IDistributionMirror, IDistributionMirrorSet, IMirrorCDImageDistroSeries, | 49 | IDistributionMirror, IDistributionMirrorSet, IMirrorCDImageDistroSeries, |
1327 | 49 | IMirrorDistroArchSeries, IMirrorDistroSeriesSource, IMirrorProbeRecord, | 50 | IMirrorDistroArchSeries, IMirrorDistroSeriesSource, IMirrorProbeRecord, |
1329 | 50 | MirrorContent, MirrorFreshness, MirrorSpeed, MirrorStatus, PROBE_INTERVAL) | 51 | MirrorContent, MirrorFreshness, MirrorHasNoHTTPURL, MirrorNotOfficial, |
1330 | 52 | MirrorNotProbed, MirrorSpeed, MirrorStatus, PROBE_INTERVAL) | ||
1331 | 51 | from lp.registry.interfaces.distroseries import IDistroSeries | 53 | from lp.registry.interfaces.distroseries import IDistroSeries |
1332 | 52 | from lp.registry.interfaces.sourcepackage import SourcePackageFileType | 54 | from lp.registry.interfaces.sourcepackage import SourcePackageFileType |
1333 | 53 | from canonical.launchpad.mail import simple_sendmail, format_address | 55 | from canonical.launchpad.mail import simple_sendmail, format_address |
1334 | @@ -100,6 +102,8 @@ | |||
1335 | 100 | date_reviewed = UtcDateTimeCol(default=None) | 102 | date_reviewed = UtcDateTimeCol(default=None) |
1336 | 101 | whiteboard = StringCol( | 103 | whiteboard = StringCol( |
1337 | 102 | notNull=False, default=None) | 104 | notNull=False, default=None) |
1338 | 105 | country_dns_mirror = BoolCol( | ||
1339 | 106 | notNull=True, default=False) | ||
1340 | 103 | 107 | ||
1341 | 104 | @property | 108 | @property |
1342 | 105 | def base_url(self): | 109 | def base_url(self): |
1343 | @@ -145,6 +149,64 @@ | |||
1344 | 145 | "This mirror has been probed and thus can't be removed.") | 149 | "This mirror has been probed and thus can't be removed.") |
1345 | 146 | SQLBase.destroySelf(self) | 150 | SQLBase.destroySelf(self) |
1346 | 147 | 151 | ||
1347 | 152 | def verifyTransitionToCountryMirror(self): | ||
1348 | 153 | """Verify that a mirror can be set as a country mirror. | ||
1349 | 154 | |||
1350 | 155 | Return True if valid, otherwise raise a subclass of | ||
1351 | 156 | CannotTransitionToCountryMirror. | ||
1352 | 157 | """ | ||
1353 | 158 | |||
1354 | 159 | current_country_mirror = self.distribution.getCountryMirror( | ||
1355 | 160 | self.country, self.content) | ||
1356 | 161 | |||
1357 | 162 | if current_country_mirror is not None: | ||
1358 | 163 | # Country already has a country mirror. | ||
1359 | 164 | raise CountryMirrorAlreadySet( | ||
1360 | 165 | "%s already has a country %s mirror set." % ( | ||
1361 | 166 | self.country.name, self.content)) | ||
1362 | 167 | |||
1363 | 168 | if not self.isOfficial(): | ||
1364 | 169 | # Only official mirrors may be set as country mirrors. | ||
1365 | 170 | raise MirrorNotOfficial( | ||
1366 | 171 | "This mirror may not be set as a country mirror as it is not " | ||
1367 | 172 | "an official mirror.") | ||
1368 | 173 | |||
1369 | 174 | if self.http_base_url is None: | ||
1370 | 175 | # Country mirrors must have HTTP URLs set. | ||
1371 | 176 | raise MirrorHasNoHTTPURL( | ||
1372 | 177 | "This mirror may not be set as a country mirror as it does " | ||
1373 | 178 | "not have an HTTP URL set.") | ||
1374 | 179 | |||
1375 | 180 | if not self.last_probe_record: | ||
1376 | 181 | # Only mirrors which have been probed may be set as country | ||
1377 | 182 | # mirrors. | ||
1378 | 183 | raise MirrorNotProbed( | ||
1379 | 184 | "This mirror may not be set as a country mirror as it has " | ||
1380 | 185 | "not been probed.") | ||
1381 | 186 | |||
1382 | 187 | # Verification done. | ||
1383 | 188 | return True | ||
1384 | 189 | |||
1385 | 190 | def canTransitionToCountryMirror(self): | ||
1386 | 191 | """See `IDistributionMirror`.""" | ||
1387 | 192 | try: | ||
1388 | 193 | return self.verifyTransitionToCountryMirror() | ||
1389 | 194 | except CannotTransitionToCountryMirror: | ||
1390 | 195 | return False | ||
1391 | 196 | |||
1392 | 197 | def transitionToCountryMirror(self, country_dns_mirror): | ||
1393 | 198 | """See `IDistributionMirror`.""" | ||
1394 | 199 | |||
1395 | 200 | # country_dns_mirror has not been changed, do nothing. | ||
1396 | 201 | if self.country_dns_mirror == country_dns_mirror: | ||
1397 | 202 | return | ||
1398 | 203 | |||
1399 | 204 | # Environment sanity checks. | ||
1400 | 205 | if country_dns_mirror: | ||
1401 | 206 | self.verifyTransitionToCountryMirror() | ||
1402 | 207 | |||
1403 | 208 | self.country_dns_mirror = country_dns_mirror | ||
1404 | 209 | |||
1405 | 148 | def getOverallFreshness(self): | 210 | def getOverallFreshness(self): |
1406 | 149 | """See IDistributionMirror""" | 211 | """See IDistributionMirror""" |
1407 | 150 | # XXX Guilherme Salgado 2006-08-16: | 212 | # XXX Guilherme Salgado 2006-08-16: |
1408 | 151 | 213 | ||
1409 | === modified file 'lib/lp/registry/model/person.py' | |||
1410 | --- lib/lp/registry/model/person.py 2010-03-24 23:19:52 +0000 | |||
1411 | +++ lib/lp/registry/model/person.py 2010-04-05 20:44:34 +0000 | |||
1412 | @@ -124,7 +124,7 @@ | |||
1413 | 124 | TeamMembershipStatus) | 124 | TeamMembershipStatus) |
1414 | 125 | from lp.registry.interfaces.wikiname import IWikiName, IWikiNameSet | 125 | from lp.registry.interfaces.wikiname import IWikiName, IWikiNameSet |
1415 | 126 | from canonical.launchpad.webapp.interfaces import ( | 126 | from canonical.launchpad.webapp.interfaces import ( |
1417 | 127 | AUTH_STORE, ILaunchBag, IStoreSelector, MASTER_FLAVOR) | 127 | ILaunchBag, IStoreSelector, MASTER_FLAVOR) |
1418 | 128 | 128 | ||
1419 | 129 | from lp.soyuz.model.archive import Archive | 129 | from lp.soyuz.model.archive import Archive |
1420 | 130 | from lp.registry.model.codeofconduct import SignedCodeOfConduct | 130 | from lp.registry.model.codeofconduct import SignedCodeOfConduct |
1421 | @@ -263,22 +263,7 @@ | |||
1422 | 263 | return '<Person at 0x%x %s (%s)>' % ( | 263 | return '<Person at 0x%x %s (%s)>' % ( |
1423 | 264 | id(self), self.name, self.displayname) | 264 | id(self), self.name, self.displayname) |
1424 | 265 | 265 | ||
1441 | 266 | def _sync_displayname(self, attr, value): | 266 | displayname = StringCol(dbName='displayname', notNull=True) |
1426 | 267 | """Update any related Account.displayname. | ||
1427 | 268 | |||
1428 | 269 | We can't do this in a DB trigger as soon the Account table will | ||
1429 | 270 | in a separate database to the Person table. | ||
1430 | 271 | """ | ||
1431 | 272 | if self.accountID is not None: | ||
1432 | 273 | auth_store = getUtility(IStoreSelector).get( | ||
1433 | 274 | AUTH_STORE, MASTER_FLAVOR) | ||
1434 | 275 | account = auth_store.get(Account, self.accountID) | ||
1435 | 276 | if account.displayname != value: | ||
1436 | 277 | account.displayname = value | ||
1437 | 278 | return value | ||
1438 | 279 | |||
1439 | 280 | displayname = StringCol(dbName='displayname', notNull=True, | ||
1440 | 281 | storm_validator=_sync_displayname) | ||
1442 | 282 | 267 | ||
1443 | 283 | teamdescription = StringCol(dbName='teamdescription', default=None) | 268 | teamdescription = StringCol(dbName='teamdescription', default=None) |
1444 | 284 | homepage_content = StringCol(default=None) | 269 | homepage_content = StringCol(default=None) |
1445 | 285 | 270 | ||
1446 | === modified file 'lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt' | |||
1447 | --- lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt 2009-12-10 23:32:13 +0000 | |||
1448 | +++ lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt 2010-04-05 20:44:34 +0000 | |||
1449 | @@ -40,13 +40,14 @@ | |||
1450 | 40 | >>> print browser.title | 40 | >>> print browser.title |
1451 | 41 | Mirrors of Ubuntu Linux... | 41 | Mirrors of Ubuntu Linux... |
1452 | 42 | >>> print_mirrors_by_countries(browser.contents) | 42 | >>> print_mirrors_by_countries(browser.contents) |
1455 | 43 | Antarctica: | 43 | Antarctica: [(u'Archive-mirror2', u'http', u'128 Kbps', |
1456 | 44 | [(u'Archive-mirror2', u'http', u'128 Kbps', u'Six hours behind')] | 44 | u'Six hours behind')] |
1457 | 45 | France: | 45 | France: |
1458 | 46 | [(u'Archive-404-mirror', u'http', u'512 Kbps', u'Last update unknown'), | 46 | [(u'Archive-404-mirror', u'http', u'512 Kbps', u'Last update unknown'), |
1462 | 47 | (u'Archive-mirror', u'http', u'128 Kbps', u'Last update unknown')] | 47 | (u'Archive-mirror', u'http', u'128 Kbps', u'Last update unknown')] |
1463 | 48 | United Kingdom: | 48 | United Kingdom: [(u'Canonical-archive', u'http', u'100 Mbps', |
1464 | 49 | [(u'Canonical-archive', u'http', u'100 Mbps', u'Last update unknown')] | 49 | u'Last update unknown')] |
1465 | 50 | |||
1466 | 50 | >>> find_tags_by_class(browser.contents, 'distromirrorstatusSIXHOURSBEHIND') | 51 | >>> find_tags_by_class(browser.contents, 'distromirrorstatusSIXHOURSBEHIND') |
1467 | 51 | [<span class="distromirrorstatusSIXHOURSBEHIND">Six hours behind</span>] | 52 | [<span class="distromirrorstatusSIXHOURSBEHIND">Six hours behind</span>] |
1468 | 52 | >>> find_tags_by_class(browser.contents, 'distromirrorstatusUNKNOWN')[0] | 53 | >>> find_tags_by_class(browser.contents, 'distromirrorstatusUNKNOWN')[0] |
1469 | @@ -59,13 +60,12 @@ | |||
1470 | 59 | >>> browser.url | 60 | >>> browser.url |
1471 | 60 | 'http://launchpad.dev/ubuntu/+cdmirrors' | 61 | 'http://launchpad.dev/ubuntu/+cdmirrors' |
1472 | 61 | >>> print_mirrors_by_countries(browser.contents) | 62 | >>> print_mirrors_by_countries(browser.contents) |
1475 | 62 | France: | 63 | France: |
1476 | 63 | [(u'Releases-mirror', u'http', u'2 Mbps'), | 64 | [(u'Releases-mirror', u'http', u'2 Mbps'), |
1477 | 64 | (u'Unreachable-mirror', u'http', u'512 Kbps')] | 65 | (u'Unreachable-mirror', u'http', u'512 Kbps')] |
1482 | 65 | Germany: | 66 | Germany: [(u'Releases-mirror2', u'http', u'2 Mbps')] |
1483 | 66 | [(u'Releases-mirror2', u'http', u'2 Mbps')] | 67 | United Kingdom: [(u'Canonical-releases', u'http', u'100 Mbps')] |
1484 | 67 | United Kingdom: | 68 | |
1481 | 68 | [(u'Canonical-releases', u'http', u'100 Mbps')] | ||
1485 | 69 | 69 | ||
1486 | 70 | === Disabled mirrors === | 70 | === Disabled mirrors === |
1487 | 71 | 71 | ||
1488 | 72 | 72 | ||
1489 | === modified file 'lib/lp/registry/stories/person/xx-admin-person-review.txt' | |||
1490 | --- lib/lp/registry/stories/person/xx-admin-person-review.txt 2010-01-30 22:39:54 +0000 | |||
1491 | +++ lib/lp/registry/stories/person/xx-admin-person-review.txt 2010-04-05 20:44:34 +0000 | |||
1492 | @@ -63,7 +63,7 @@ | |||
1493 | 63 | >>> print admin_browser.title | 63 | >>> print admin_browser.title |
1494 | 64 | The one and only Salgado does not use Launchpad | 64 | The one and only Salgado does not use Launchpad |
1495 | 65 | >>> print get_feedback_messages(admin_browser.contents)[0] | 65 | >>> print get_feedback_messages(admin_browser.contents)[0] |
1497 | 66 | The account "The one and only Salgado" has been suspended. | 66 | The account "Guilherme Salgado" has been suspended. |
1498 | 67 | 67 | ||
1499 | 68 | The admin can see the account information of a user that does not use | 68 | The admin can see the account information of a user that does not use |
1500 | 69 | Launchpad, and can change the account too. Note that all pages that belong | 69 | Launchpad, and can change the account too. Note that all pages that belong |
1501 | 70 | 70 | ||
1502 | === modified file 'lib/lp/registry/stories/webservice/xx-distribution-mirror.txt' | |||
1503 | --- lib/lp/registry/stories/webservice/xx-distribution-mirror.txt 2010-02-23 19:40:45 +0000 | |||
1504 | +++ lib/lp/registry/stories/webservice/xx-distribution-mirror.txt 2010-04-05 20:44:34 +0000 | |||
1505 | @@ -8,10 +8,12 @@ | |||
1506 | 8 | >>> distro = distros['entries'][0] | 8 | >>> distro = distros['entries'][0] |
1507 | 9 | >>> ubuntu = webservice.get(distro['self_link']).jsonBody() | 9 | >>> ubuntu = webservice.get(distro['self_link']).jsonBody() |
1508 | 10 | >>> ubuntu_archive_mirrors = webservice.get(ubuntu['archive_mirrors_collection_link']).jsonBody() | 10 | >>> ubuntu_archive_mirrors = webservice.get(ubuntu['archive_mirrors_collection_link']).jsonBody() |
1512 | 11 | >>> canonical_archive = ubuntu_archive_mirrors['entries'][0] | 11 | >>> canonical_archive = webservice.named_get( |
1513 | 12 | >>> canonical_archive_json = webservice.get(canonical_archive['self_link']).jsonBody() | 12 | ... ubuntu['self_link'], 'getMirrorByName', |
1514 | 13 | >>> pprint_entry(canonical_archive_json) | 13 | ... name='canonical-archive').jsonBody() |
1515 | 14 | >>> pprint_entry(canonical_archive) | ||
1516 | 14 | content: u'Archive' | 15 | content: u'Archive' |
1517 | 16 | country_dns_mirror: False | ||
1518 | 15 | country_link: u'http://.../+countries/GB' | 17 | country_link: u'http://.../+countries/GB' |
1519 | 16 | date_created: u'2006-10-16T18:31:43.434567+00:00' | 18 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1520 | 17 | date_reviewed: None | 19 | date_reviewed: None |
1521 | @@ -39,6 +41,7 @@ | |||
1522 | 39 | >>> canonical_releases_json = webservice.get(canonical_releases['self_link']).jsonBody() | 41 | >>> canonical_releases_json = webservice.get(canonical_releases['self_link']).jsonBody() |
1523 | 40 | >>> pprint_entry(canonical_releases_json) | 42 | >>> pprint_entry(canonical_releases_json) |
1524 | 41 | content: u'CD Image' | 43 | content: u'CD Image' |
1525 | 44 | country_dns_mirror: False | ||
1526 | 42 | country_link: u'http://.../+countries/GB' | 45 | country_link: u'http://.../+countries/GB' |
1527 | 43 | date_created: u'2006-10-16T18:31:43.434567+00:00' | 46 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1528 | 44 | date_reviewed: None | 47 | date_reviewed: None |
1529 | @@ -73,12 +76,12 @@ | |||
1530 | 73 | >>> karl_db = getUtility(IPersonSet).getByName('karl') | 76 | >>> karl_db = getUtility(IPersonSet).getByName('karl') |
1531 | 74 | >>> test_db = getUtility(IPersonSet).getByName('name12') | 77 | >>> test_db = getUtility(IPersonSet).getByName('name12') |
1532 | 75 | >>> no_priv_db = getUtility(IPersonSet).getByName('no-priv') | 78 | >>> no_priv_db = getUtility(IPersonSet).getByName('no-priv') |
1539 | 76 | >>> karl_webservice = webservice_for_person(karl_db, | 79 | >>> karl_webservice = webservice_for_person( |
1540 | 77 | ... permission=OAuthPermission.WRITE_PUBLIC) | 80 | ... karl_db, permission=OAuthPermission.WRITE_PUBLIC) |
1541 | 78 | >>> test_webservice = webservice_for_person(test_db, | 81 | >>> test_webservice = webservice_for_person( |
1542 | 79 | ... permission=OAuthPermission.WRITE_PUBLIC) | 82 | ... test_db, permission=OAuthPermission.WRITE_PUBLIC) |
1543 | 80 | >>> no_priv_webservice = webservice_for_person(no_priv_db, | 83 | >>> no_priv_webservice = webservice_for_person( |
1544 | 81 | ... permission=OAuthPermission.READ_PUBLIC) | 84 | ... no_priv_db, permission=OAuthPermission.READ_PUBLIC) |
1545 | 82 | >>> logout() | 85 | >>> logout() |
1546 | 83 | 86 | ||
1547 | 84 | Ensure that anonymous API sessions can view mirror listings; archive/releases. | 87 | Ensure that anonymous API sessions can view mirror listings; archive/releases. |
1548 | @@ -97,7 +100,9 @@ | |||
1549 | 97 | 100 | ||
1550 | 98 | One must have special permissions to access certain attributes: | 101 | One must have special permissions to access certain attributes: |
1551 | 99 | 102 | ||
1553 | 100 | >>> archive_404_mirror = ubuntu_archive_mirrors['entries'][1] | 103 | >>> archive_404_mirror = webservice.named_get( |
1554 | 104 | ... ubuntu['self_link'], 'getMirrorByName', | ||
1555 | 105 | ... name="archive-404-mirror").jsonBody() | ||
1556 | 101 | >>> response = no_priv_webservice.get( | 106 | >>> response = no_priv_webservice.get( |
1557 | 102 | ... archive_404_mirror['self_link']).jsonBody() | 107 | ... archive_404_mirror['self_link']).jsonBody() |
1558 | 103 | >>> pprint_entry(response) | 108 | >>> pprint_entry(response) |
1559 | @@ -128,6 +133,7 @@ | |||
1560 | 128 | ... archive_404_mirror['self_link']).jsonBody() | 133 | ... archive_404_mirror['self_link']).jsonBody() |
1561 | 129 | >>> pprint_entry(response) | 134 | >>> pprint_entry(response) |
1562 | 130 | content: u'Archive' | 135 | content: u'Archive' |
1563 | 136 | country_dns_mirror: False | ||
1564 | 131 | country_link: u'http://.../+countries/FR' | 137 | country_link: u'http://.../+countries/FR' |
1565 | 132 | date_created: u'2006-10-16T18:31:43.438573+00:00' | 138 | date_created: u'2006-10-16T18:31:43.438573+00:00' |
1566 | 133 | date_reviewed: None | 139 | date_reviewed: None |
1567 | @@ -209,6 +215,7 @@ | |||
1568 | 209 | ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody() | 215 | ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody() |
1569 | 210 | >>> pprint_entry(response) | 216 | >>> pprint_entry(response) |
1570 | 211 | content: u'CD Image' | 217 | content: u'CD Image' |
1571 | 218 | country_dns_mirror: False | ||
1572 | 212 | country_link: u'http://.../+countries/GL' | 219 | country_link: u'http://.../+countries/GL' |
1573 | 213 | date_created: u'2006-10-16T18:31:43.434567+00:00' | 220 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1574 | 214 | date_reviewed: None | 221 | date_reviewed: None |
1575 | @@ -244,7 +251,9 @@ | |||
1576 | 244 | "getOverallFreshness" returns the freshness of the mirror determined by the | 251 | "getOverallFreshness" returns the freshness of the mirror determined by the |
1577 | 245 | mirror prober from the mirror's last probe. | 252 | mirror prober from the mirror's last probe. |
1578 | 246 | 253 | ||
1580 | 247 | >>> releases_mirror2 = ubuntu_cd_mirrors['entries'][2] | 254 | >>> releases_mirror2 = webservice.named_get( |
1581 | 255 | ... ubuntu['self_link'], 'getMirrorByName', | ||
1582 | 256 | ... name='releases-mirror2').jsonBody() | ||
1583 | 248 | >>> freshness = webservice.named_get(releases_mirror2['self_link'], | 257 | >>> freshness = webservice.named_get(releases_mirror2['self_link'], |
1584 | 249 | ... 'getOverallFreshness').jsonBody() | 258 | ... 'getOverallFreshness').jsonBody() |
1585 | 250 | >>> print freshness | 259 | >>> print freshness |
1586 | 251 | 260 | ||
1587 | === modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt' | |||
1588 | --- lib/lp/registry/stories/webservice/xx-distribution.txt 2010-02-23 17:36:27 +0000 | |||
1589 | +++ lib/lp/registry/stories/webservice/xx-distribution.txt 2010-04-05 20:44:34 +0000 | |||
1590 | @@ -123,6 +123,7 @@ | |||
1591 | 123 | ... name='canonical-releases').jsonBody() | 123 | ... name='canonical-releases').jsonBody() |
1592 | 124 | >>> pprint_entry(canonical_releases) | 124 | >>> pprint_entry(canonical_releases) |
1593 | 125 | content: u'CD Image' | 125 | content: u'CD Image' |
1594 | 126 | country_dns_mirror: False | ||
1595 | 126 | country_link: u'http://.../+countries/GB' | 127 | country_link: u'http://.../+countries/GB' |
1596 | 127 | date_created: u'2006-10-16T18:31:43.434567+00:00' | 128 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1597 | 128 | date_reviewed: None | 129 | date_reviewed: None |
1598 | @@ -142,3 +143,76 @@ | |||
1599 | 142 | speed: u'100 Mbps' | 143 | speed: u'100 Mbps' |
1600 | 143 | status: u'Official' | 144 | status: u'Official' |
1601 | 144 | whiteboard: None | 145 | whiteboard: None |
1602 | 146 | |||
1603 | 147 | "getCountryMirror" returns the country DNS mirror for a given country; | ||
1604 | 148 | returning None if there isn't one. | ||
1605 | 149 | |||
1606 | 150 | >>> # Prepare stuff. | ||
1607 | 151 | >>> from lp.registry.interfaces.distribution import IDistributionSet | ||
1608 | 152 | >>> from zope.component import getUtility | ||
1609 | 153 | >>> from canonical.launchpad.testing.pages import webservice_for_person | ||
1610 | 154 | >>> from canonical.launchpad.webapp.interfaces import OAuthPermission | ||
1611 | 155 | >>> from lp.registry.interfaces.person import IPersonSet | ||
1612 | 156 | >>> from simplejson import dumps | ||
1613 | 157 | |||
1614 | 158 | >>> login('admin@canonical.com') | ||
1615 | 159 | >>> ubuntu_distro = getUtility(IDistributionSet).getByName('ubuntu') | ||
1616 | 160 | >>> showa_station = factory.makeMirror(ubuntu_distro, | ||
1617 | 161 | ... "Showa Station", country=9, | ||
1618 | 162 | ... http_url="http://mirror.showa.antarctica.org/ubuntu", | ||
1619 | 163 | ... official_candidate=True) | ||
1620 | 164 | >>> showa_station_log = factory.makeMirrorProbeRecord(showa_station) | ||
1621 | 165 | >>> logout() | ||
1622 | 166 | |||
1623 | 167 | >>> login(ANONYMOUS) | ||
1624 | 168 | >>> karl_db = getUtility(IPersonSet).getByName('karl') | ||
1625 | 169 | >>> karl_webservice = webservice_for_person(karl_db, | ||
1626 | 170 | ... permission=OAuthPermission.WRITE_PUBLIC) | ||
1627 | 171 | >>> logout() | ||
1628 | 172 | |||
1629 | 173 | >>> # Mark new mirror as official and a country mirror. | ||
1630 | 174 | >>> patch = { | ||
1631 | 175 | ... u'status': 'Official', | ||
1632 | 176 | ... u'country_dns_mirror': True | ||
1633 | 177 | ... } | ||
1634 | 178 | |||
1635 | 179 | >>> antarctica_patch_target = webservice.named_get( | ||
1636 | 180 | ... ubuntu['self_link'], 'getMirrorByName', | ||
1637 | 181 | ... name='mirror.showa.antarctica.org-archive').jsonBody() | ||
1638 | 182 | ... ) | ||
1639 | 183 | |||
1640 | 184 | >>> response = karl_webservice.patch( | ||
1641 | 185 | ... antarctica_patch_target['self_link'], 'application/json', | ||
1642 | 186 | ... dumps(patch)) | ||
1643 | 187 | |||
1644 | 188 | >>> antarctica = webservice.get("/+countries/AQ").jsonBody() | ||
1645 | 189 | >>> antarctica_country_mirror_archive = webservice.named_get( | ||
1646 | 190 | ... ubuntu['self_link'], 'getCountryMirror', | ||
1647 | 191 | ... country=antarctica['self_link'], | ||
1648 | 192 | ... mirror_type="Archive").jsonBody() | ||
1649 | 193 | >>> pprint_entry(antarctica_country_mirror_archive) | ||
1650 | 194 | content: u'Archive' | ||
1651 | 195 | country_dns_mirror: True | ||
1652 | 196 | country_link: u'http://.../+countries/AQ' | ||
1653 | 197 | ... | ||
1654 | 198 | |||
1655 | 199 | >>> uk = webservice.get("/+countries/GB").jsonBody() | ||
1656 | 200 | >>> uk_country_mirror_archive = webservice.named_get( | ||
1657 | 201 | ... ubuntu['self_link'], 'getCountryMirror', | ||
1658 | 202 | ... country=uk['self_link'], | ||
1659 | 203 | ... mirror_type="Archive") | ||
1660 | 204 | >>> print uk_country_mirror_archive.jsonBody() | ||
1661 | 205 | None | ||
1662 | 206 | |||
1663 | 207 | For "getCountryMirror", the mirror_type parameter must be "Archive" or | ||
1664 | 208 | "CD Images": | ||
1665 | 209 | |||
1666 | 210 | >>> uk_country_mirror_archive = webservice.named_get( | ||
1667 | 211 | ... ubuntu['self_link'], 'getCountryMirror', | ||
1668 | 212 | ... country=uk['self_link'], | ||
1669 | 213 | ... mirror_type="Bogus") | ||
1670 | 214 | >>> print uk_country_mirror_archive.jsonBody() | ||
1671 | 215 | Traceback (most recent call last): | ||
1672 | 216 | ... | ||
1673 | 217 | ValueError: mirror_type: Invalid value "Bogus". Acceptable values are: | ||
1674 | 218 | Archive, CD Image | ||
1675 | 145 | 219 | ||
1676 | === modified file 'lib/lp/registry/tests/test_distributionmirror.py' | |||
1677 | --- lib/lp/registry/tests/test_distributionmirror.py 2009-10-26 18:40:04 +0000 | |||
1678 | +++ lib/lp/registry/tests/test_distributionmirror.py 2010-04-05 20:44:34 +0000 | |||
1679 | @@ -3,7 +3,6 @@ | |||
1680 | 3 | 3 | ||
1681 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
1682 | 5 | 5 | ||
1683 | 6 | from StringIO import StringIO | ||
1684 | 7 | import unittest | 6 | import unittest |
1685 | 8 | 7 | ||
1686 | 9 | import transaction | 8 | import transaction |
1687 | @@ -17,19 +16,19 @@ | |||
1688 | 17 | from lp.registry.interfaces.distributionmirror import ( | 16 | from lp.registry.interfaces.distributionmirror import ( |
1689 | 18 | IDistributionMirrorSet, MirrorContent, MirrorFreshness) | 17 | IDistributionMirrorSet, MirrorContent, MirrorFreshness) |
1690 | 19 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | 18 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1691 | 20 | from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet | ||
1692 | 21 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 19 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
1693 | 22 | from lp.registry.interfaces.distribution import IDistributionSet | 20 | from lp.registry.interfaces.distribution import IDistributionSet |
1694 | 23 | from lp.services.mail import stub | 21 | from lp.services.mail import stub |
1695 | 22 | from lp.testing.factory import LaunchpadObjectFactory | ||
1696 | 24 | 23 | ||
1697 | 25 | from canonical.testing import LaunchpadFunctionalLayer | 24 | from canonical.testing import LaunchpadFunctionalLayer |
1698 | 26 | 25 | ||
1699 | 27 | |||
1700 | 28 | class TestDistributionMirror(unittest.TestCase): | 26 | class TestDistributionMirror(unittest.TestCase): |
1701 | 29 | layer = LaunchpadFunctionalLayer | 27 | layer = LaunchpadFunctionalLayer |
1702 | 30 | 28 | ||
1703 | 31 | def setUp(self): | 29 | def setUp(self): |
1704 | 32 | login('test@canonical.com') | 30 | login('test@canonical.com') |
1705 | 31 | self.factory = LaunchpadObjectFactory() | ||
1706 | 33 | mirrorset = getUtility(IDistributionMirrorSet) | 32 | mirrorset = getUtility(IDistributionMirrorSet) |
1707 | 34 | self.cdimage_mirror = getUtility(IDistributionMirrorSet).getByName( | 33 | self.cdimage_mirror = getUtility(IDistributionMirrorSet).getByName( |
1708 | 35 | 'releases-mirror') | 34 | 'releases-mirror') |
1709 | @@ -132,15 +131,6 @@ | |||
1710 | 132 | self.archive_mirror.getOverallFreshness(), | 131 | self.archive_mirror.getOverallFreshness(), |
1711 | 133 | MirrorFreshness.TWODAYSBEHIND) | 132 | MirrorFreshness.TWODAYSBEHIND) |
1712 | 134 | 133 | ||
1713 | 135 | def _create_probe_record(self, mirror): | ||
1714 | 136 | log_file = StringIO() | ||
1715 | 137 | log_file.write("Fake probe, nothing useful here.") | ||
1716 | 138 | log_file.seek(0) | ||
1717 | 139 | library_alias = getUtility(ILibraryFileAliasSet).create( | ||
1718 | 140 | name='foo', size=len(log_file.getvalue()), | ||
1719 | 141 | file=log_file, contentType='text/plain') | ||
1720 | 142 | proberecord = mirror.newProbeRecord(library_alias) | ||
1721 | 143 | |||
1722 | 144 | def test_disabling_mirror_and_notifying_owner(self): | 134 | def test_disabling_mirror_and_notifying_owner(self): |
1723 | 145 | login('karl@canonical.com') | 135 | login('karl@canonical.com') |
1724 | 146 | 136 | ||
1725 | @@ -148,7 +138,7 @@ | |||
1726 | 148 | # If a mirror has been probed only once, the owner will always be | 138 | # If a mirror has been probed only once, the owner will always be |
1727 | 149 | # notified when it's disabled --it doesn't matter whether it was | 139 | # notified when it's disabled --it doesn't matter whether it was |
1728 | 150 | # previously enabled or disabled. | 140 | # previously enabled or disabled. |
1730 | 151 | self._create_probe_record(mirror) | 141 | self.factory.makeMirrorProbeRecord(mirror) |
1731 | 152 | self.failUnless(mirror.enabled) | 142 | self.failUnless(mirror.enabled) |
1732 | 153 | log = 'Got a 404 on http://foo/baz' | 143 | log = 'Got a 404 on http://foo/baz' |
1733 | 154 | mirror.disable(notify_owner=True, log=log) | 144 | mirror.disable(notify_owner=True, log=log) |
1734 | @@ -166,7 +156,7 @@ | |||
1735 | 166 | 156 | ||
1736 | 167 | # For mirrors that have been probed more than once, we'll only notify | 157 | # For mirrors that have been probed more than once, we'll only notify |
1737 | 168 | # the owner if the mirror was previously enabled. | 158 | # the owner if the mirror was previously enabled. |
1739 | 169 | self._create_probe_record(mirror) | 159 | self.factory.makeMirrorProbeRecord(mirror) |
1740 | 170 | mirror.enabled = True | 160 | mirror.enabled = True |
1741 | 171 | mirror.disable(notify_owner=True, log=log) | 161 | mirror.disable(notify_owner=True, log=log) |
1742 | 172 | # A notification was sent to the owner and other to the mirror admins. | 162 | # A notification was sent to the owner and other to the mirror admins. |
1743 | 173 | 163 | ||
1744 | === modified file 'lib/lp/registry/tests/test_personset.py' | |||
1745 | --- lib/lp/registry/tests/test_personset.py 2010-03-11 20:54:36 +0000 | |||
1746 | +++ lib/lp/registry/tests/test_personset.py 2010-04-05 20:44:34 +0000 | |||
1747 | @@ -99,23 +99,19 @@ | |||
1748 | 99 | # Person in question. | 99 | # Person in question. |
1749 | 100 | 100 | ||
1750 | 101 | # Create a testing `Account` and a testing `Person` directly, | 101 | # Create a testing `Account` and a testing `Person` directly, |
1753 | 102 | # linked. However the `Account` email is not linked to the | 102 | # linked. |
1752 | 103 | # `Person`. | ||
1754 | 104 | testing_account = self.factory.makeAccount( | 103 | testing_account = self.factory.makeAccount( |
1755 | 105 | self.displayname, email=self.email_address) | 104 | self.displayname, email=self.email_address) |
1756 | 106 | testing_person = removeSecurityProxy( | 105 | testing_person = removeSecurityProxy( |
1757 | 107 | testing_account).createPerson(self.rationale) | 106 | testing_account).createPerson(self.rationale) |
1760 | 108 | self.assertIs(None, testing_account.preferredemail.person) | 107 | self.assertEqual( |
1761 | 109 | self.assertIs(None, testing_person.preferredemail) | 108 | testing_person, testing_account.preferredemail.person) |
1762 | 110 | 109 | ||
1763 | 110 | # Since there's an existing Person for the given email address, | ||
1764 | 111 | # IPersonSet.ensurePerson() will just return it. | ||
1765 | 111 | ensured_person = self.person_set.ensurePerson( | 112 | ensured_person = self.person_set.ensurePerson( |
1766 | 112 | self.email_address, self.displayname, self.rationale) | 113 | self.email_address, self.displayname, self.rationale) |
1773 | 113 | 114 | self.assertEqual(testing_person, ensured_person) | |
1768 | 114 | # The existing Person was retrieved and the Account | ||
1769 | 115 | # 'preferredemail' is also bound to the existing Person. | ||
1770 | 116 | self.assertEquals(testing_person.id, ensured_person.id) | ||
1771 | 117 | self.assertEquals(testing_account.preferredemail.id, | ||
1772 | 118 | ensured_person.preferredemail.id) | ||
1774 | 119 | 115 | ||
1775 | 120 | 116 | ||
1776 | 121 | class TestPersonSetMerge(TestCaseWithFactory): | 117 | class TestPersonSetMerge(TestCaseWithFactory): |
1777 | 122 | 118 | ||
1778 | === modified file 'lib/lp/testing/factory.py' | |||
1779 | --- lib/lp/testing/factory.py 2010-03-26 21:03:30 +0000 | |||
1780 | +++ lib/lp/testing/factory.py 2010-04-05 20:44:34 +0000 | |||
1781 | @@ -1992,8 +1992,22 @@ | |||
1782 | 1992 | team_list = self.makeMailingList(team, owner) | 1992 | team_list = self.makeMailingList(team, owner) |
1783 | 1993 | return team, team_list | 1993 | return team, team_list |
1784 | 1994 | 1994 | ||
1785 | 1995 | def makeMirrorProbeRecord(self, mirror): | ||
1786 | 1996 | """Create a probe record for a mirror of a distribution.""" | ||
1787 | 1997 | log_file = StringIO() | ||
1788 | 1998 | log_file.write("Fake probe, nothing useful here.") | ||
1789 | 1999 | log_file.seek(0) | ||
1790 | 2000 | |||
1791 | 2001 | library_alias = getUtility(ILibraryFileAliasSet).create( | ||
1792 | 2002 | name='foo', size=len(log_file.getvalue()), | ||
1793 | 2003 | file=log_file, contentType='text/plain') | ||
1794 | 2004 | |||
1795 | 2005 | proberecord = mirror.newProbeRecord(library_alias) | ||
1796 | 2006 | return proberecord | ||
1797 | 2007 | |||
1798 | 1995 | def makeMirror(self, distribution, displayname, country=None, | 2008 | def makeMirror(self, distribution, displayname, country=None, |
1800 | 1996 | http_url=None, ftp_url=None, rsync_url=None): | 2009 | http_url=None, ftp_url=None, rsync_url=None, |
1801 | 2010 | official_candidate=False): | ||
1802 | 1997 | """Create a mirror for the distribution.""" | 2011 | """Create a mirror for the distribution.""" |
1803 | 1998 | # If no URL is specified create an HTTP URL. | 2012 | # If no URL is specified create an HTTP URL. |
1804 | 1999 | if http_url is None and ftp_url is None and rsync_url is None: | 2013 | if http_url is None and ftp_url is None and rsync_url is None: |
1805 | @@ -2012,7 +2026,7 @@ | |||
1806 | 2012 | http_base_url=http_url, | 2026 | http_base_url=http_url, |
1807 | 2013 | ftp_base_url=ftp_url, | 2027 | ftp_base_url=ftp_url, |
1808 | 2014 | rsync_base_url=rsync_url, | 2028 | rsync_base_url=rsync_url, |
1810 | 2015 | official_candidate=False) | 2029 | official_candidate=official_candidate) |
1811 | 2016 | return mirror | 2030 | return mirror |
1812 | 2017 | 2031 | ||
1813 | 2018 | def makeUniqueRFC822MsgId(self): | 2032 | def makeUniqueRFC822MsgId(self): |
1814 | 2019 | 2033 | ||
1815 | === modified file 'lib/lp/testopenid/browser/server.py' | |||
1816 | --- lib/lp/testopenid/browser/server.py 2010-02-24 12:52:08 +0000 | |||
1817 | +++ lib/lp/testopenid/browser/server.py 2010-04-05 20:44:34 +0000 | |||
1818 | @@ -6,7 +6,7 @@ | |||
1819 | 6 | __all__ = [ | 6 | __all__ = [ |
1820 | 7 | 'PersistentIdentityView', | 7 | 'PersistentIdentityView', |
1821 | 8 | 'TestOpenIDApplicationNavigation', | 8 | 'TestOpenIDApplicationNavigation', |
1823 | 9 | 'TestOpenIDIndexView' | 9 | 'TestOpenIDIndexView', |
1824 | 10 | 'TestOpenIDLoginView', | 10 | 'TestOpenIDLoginView', |
1825 | 11 | 'TestOpenIDRootUrlData', | 11 | 'TestOpenIDRootUrlData', |
1826 | 12 | 'TestOpenIDView', | 12 | 'TestOpenIDView', |
1827 | @@ -21,6 +21,7 @@ | |||
1828 | 21 | from zope.security.proxy import isinstance as zisinstance | 21 | from zope.security.proxy import isinstance as zisinstance |
1829 | 22 | from zope.session.interfaces import ISession | 22 | from zope.session.interfaces import ISession |
1830 | 23 | 23 | ||
1831 | 24 | from openid import oidutil | ||
1832 | 24 | from openid.server.server import CheckIDRequest, Server | 25 | from openid.server.server import CheckIDRequest, Server |
1833 | 25 | from openid.store.memstore import MemoryStore | 26 | from openid.store.memstore import MemoryStore |
1834 | 26 | 27 | ||
1835 | @@ -47,6 +48,10 @@ | |||
1836 | 47 | openid_store = MemoryStore() | 48 | openid_store = MemoryStore() |
1837 | 48 | 49 | ||
1838 | 49 | 50 | ||
1839 | 51 | # Shut up noisy OpenID library | ||
1840 | 52 | oidutil.log = lambda message, level=0: None | ||
1841 | 53 | |||
1842 | 54 | |||
1843 | 50 | class TestOpenIDRootUrlData: | 55 | class TestOpenIDRootUrlData: |
1844 | 51 | """`ICanonicalUrlData` for the test OpenID provider.""" | 56 | """`ICanonicalUrlData` for the test OpenID provider.""" |
1845 | 52 | 57 | ||
1846 | 53 | 58 | ||
1847 | === modified file 'utilities/sourcedeps.conf' | |||
1848 | --- utilities/sourcedeps.conf 2010-03-26 17:53:41 +0000 | |||
1849 | +++ utilities/sourcedeps.conf 2010-04-05 20:44:34 +0000 | |||
1850 | @@ -15,5 +15,4 @@ | |||
1851 | 15 | subunit lp:~launchpad-pqm/subunit/trunk;revno=61 | 15 | subunit lp:~launchpad-pqm/subunit/trunk;revno=61 |
1852 | 16 | subvertpy lp:~launchpad-pqm/subvertpy/trunk;revno=2040 | 16 | subvertpy lp:~launchpad-pqm/subvertpy/trunk;revno=2040 |
1853 | 17 | testresources lp:~launchpad-pqm/testresources/dev;revno=16 | 17 | testresources lp:~launchpad-pqm/testresources/dev;revno=16 |
1854 | 18 | canonical-identity-provider lp:~launchpad-pqm/canonical-identity-provider/trunk;revno=8903 optional | ||
1855 | 19 | shipit lp:~launchpad-pqm/shipit/trunk;revno=8903 optional | 18 | shipit lp:~launchpad-pqm/shipit/trunk;revno=8903 optional |
This branch exports a bug's message_count in the API.