← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/noro into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/noro into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #978768 in Launchpad itself: "read-only mode code is now dead code"
  https://bugs.launchpad.net/launchpad/+bug/978768

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/noro/+merge/118867

This branch rips out all the non-config-related read-only mode code, since fastdowntime has rendered read-only mode obsolete. Pretty simple.
-- 
https://code.launchpad.net/~wgrant/launchpad/noro/+merge/118867
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/noro into lp:launchpad.
=== modified file 'lib/lp/app/browser/configure.zcml'
--- lib/lp/app/browser/configure.zcml	2012-05-16 21:40:42 +0000
+++ lib/lp/app/browser/configure.zcml	2012-08-09 04:24:19 +0000
@@ -301,6 +301,7 @@
       name="index.html"
       permission="zope.Public"
       class="lp.services.webapp.login.UnauthorizedView"
+      template="../templates/launchpad-forbidden.pt"
       attribute="__call__"
       />
 
@@ -421,15 +422,6 @@
       class="lp.services.webapp.error.TranslationUnavailableView"
       />
 
-  <!-- ReadOnlyModeViolation -->
-  <browser:page
-      for="lp.services.webapp.interfaces.ReadOnlyModeViolation"
-      name="index.html"
-      permission="zope.Public"
-      template="../templates/launchpad-readonlyfailure.pt"
-      class="lp.services.webapp.error.ReadOnlyErrorView"
-      />
-
   <!-- Vocabularies -->
   <browser:page
     for="*"

=== removed file 'lib/lp/app/stories/basics/xx-read-only-mode.txt'
--- lib/lp/app/stories/basics/xx-read-only-mode.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/app/stories/basics/xx-read-only-mode.txt	1970-01-01 00:00:00 +0000
@@ -1,150 +0,0 @@
-= Read-Only Mode =
-
-During upgrades, Launchpad can be put into read-only mode using a config
-file switch. When in read-only mode, queries can be made to the slave
-database but attempts to access the master database or make database
-changes fail, returning an error page to the user.
-
-    >>> from lp.services.database.tests.readonly import (
-    ...     touch_read_only_file, remove_read_only_file)
-    >>> touch_read_only_file()
-
-
-== Notification of read-only mode. ==
-
-Users are warned when Launchpad is running in read-only mode.
-
-    >>> user_browser.open('http://launchpad.dev')
-    >>> print extract_text(first_tag_by_class(
-    ...     user_browser.contents, 'warning message'))
-    Launchpad is undergoing maintenance
-    ...
-
-Anonymous users are also warned, or they might try to signup.
-
-    >>> anon_browser.open("http://launchpad.dev/~name16";)
-    >>> print extract_text(first_tag_by_class(
-    ...     anon_browser.contents, 'warning message'))
-    Launchpad is undergoing maintenance
-    ...
-
-There is no warning when Launchpad is running normally.
-
-    >>> remove_read_only_file()
-    >>> user_browser.open('http://launchpad.dev')
-    >>> print first_tag_by_class(
-    ...     user_browser.contents, 'warning message')
-    None
-
-== Operations requiring write permissions fail ==
-
-In read-only mode, all requests for non-read permissions are denied.
-This causes edit buttons and similar to not be displayed.
-
-    >>> touch_read_only_file()
-    >>> user_browser.open('http://launchpad.dev/people/+me')
-    >>> user_browser.getLink('Change details')
-    Traceback (most recent call last):
-    ...
-    LinkNotFoundError
-
-Even if the user manages to follow a link to a form, such as clicking
-on a link rendered before read-only mode was switched on or someone
-forgetting to properly protect the edit buttons, the form is replaced
-with a nice 503 error page informing the user what is going on.
-
-    >>> remove_read_only_file()
-    >>> user_browser.open('http://launchpad.dev/people/+me')
-    >>> edit_link = user_browser.getLink('Change details')
-    >>> edit_link is None
-    False
-    >>> # XXX StuartBishop 20090423 bug=365378: raiseHttpErrors is broken,
-    >>> # requiring the try/except dance.
-    >>> user_browser.handleErrors = True
-    >>> user_browser.raiseHttpErrors = False
-    >>> touch_read_only_file()
-    >>> try:
-    ...     edit_link.click()
-    ... except:
-    ...     pass
-    >>> print user_browser.headers.get('Status')
-    503 Service Unavailable
-    >>> print extract_text(first_tag_by_class(
-    ...     user_browser.contents, 'exception'))
-    Sorry, you can't do this right now
-
-And even if a user manages to get a form and submit it, they get the
-same 503 error page.
-
-    >>> remove_read_only_file()
-    >>> user_browser.handleErrors = True
-    >>> user_browser.open('http://launchpad.dev/people/+me')
-    >>> user_browser.getLink('Change details').click()
-    >>> user_browser.getControl(name='field.displayname').value = 'Different'
-    >>> user_browser.raiseHttpErrors = False
-    >>> # XXX StuartBishop 20090423 bug=365378: raiseHttpErrors is broken,
-    >>> # requiring the try/except dance.
-    >>> touch_read_only_file()
-    >>> try:
-    ...     user_browser.getControl('Save Changes').click()
-    ... except:
-    ...     pass
-    >>> print user_browser.headers.get('Status')
-    503 Service Unavailable
-    >>> print extract_text(first_tag_by_class(
-    ...     user_browser.contents, 'exception'))
-    Sorry, you can't do this right now
-
-There are actually two exceptions that might trigger this error page.
-
-    * Legacy code may trigger the ReadOnlyModeViolation exception by
-      attempting to write to an object retrieved from the default Store. 
-
-    * Code may trigger the ReadOnlyModeDisallowedStore exception by
-      requesting a master Store.
-
-Unfortunately it is difficult to ensure the same exception will
-continue to be raised by the above test. Instead, we confirm that both
-exceptions are rendered using the same view ensuring that the observed
-behavior is the same.
-
-    >>> from zope.app import zapi
-    >>> from lp.services.webapp.interfaces import (
-    ...     ReadOnlyModeDisallowedStore, ReadOnlyModeViolation)
-    >>> from lp.services.webapp.servers import LaunchpadTestRequest
-    >>> request = LaunchpadTestRequest()
-    >>> view_name = zapi.queryDefaultViewName(
-    ...     ReadOnlyModeDisallowedStore, request)
-    >>> view_name is not None
-    True
-    >>> disallowed_view = zapi.queryMultiAdapter(
-    ...     (ReadOnlyModeDisallowedStore, request), name=view_name)
-
-    >>> view_name = zapi.queryDefaultViewName(
-    ...     ReadOnlyModeViolation, request)
-    >>> view_name is not None
-    True
-    >>> violation_view = zapi.queryMultiAdapter(
-    ...     (ReadOnlyModeViolation, request), name=view_name)
-
-    >>> violation_view == disallowed_view
-    True
-
-
-== Read-only pages ==
-
-Most Launchpad pages (the ones that don't handle edit forms) can be
-accessed in read-only mode. Here are some examples.
-
-=== Bug page ===
-
-    >>> user_browser.open('http://launchpad.dev/bugs/5')
-    >>> print user_browser.title
-    Bug #5...
-    >>> print user_browser.headers['status']
-    200 Ok
-
-
-== Cleanup ==
-
-    >>> remove_read_only_file()

=== modified file 'lib/lp/services/config/__init__.py'
--- lib/lp/services/config/__init__.py	2012-06-29 08:40:05 +0000
+++ lib/lp/services/config/__init__.py	2012-08-09 04:24:19 +0000
@@ -433,23 +433,11 @@
 
     @property
     def main_master(self):
-        # Its a bit silly having ro_main_master and rw_main_master.
-        # rw_main_master will never be used, as read-only-mode will
-        # fail attempts to access the master database with a
-        # ReadOnlyModeDisallowedStore exception.
-        from lp.services.database.readonly import is_read_only
-        if is_read_only():
-            return self.ro_main_master
-        else:
-            return self.rw_main_master
+        return self.rw_main_master
 
     @property
     def main_slave(self):
-        from lp.services.database.readonly import is_read_only
-        if is_read_only():
-            return self.ro_main_slave
-        else:
-            return self.rw_main_slave
+        return self.rw_main_slave
 
     def override(self, **kwargs):
         """Override one or more config attributes.

=== modified file 'lib/lp/services/config/tests/test_database_config.py'
--- lib/lp/services/config/tests/test_database_config.py	2012-04-06 17:28:25 +0000
+++ lib/lp/services/config/tests/test_database_config.py	2012-08-09 04:24:19 +0000
@@ -4,11 +4,6 @@
 __metaclass__ = type
 
 from lp.services.config import DatabaseConfig
-from lp.services.database.readonly import read_only_file_exists
-from lp.services.database.tests.readonly import (
-    remove_read_only_file,
-    touch_read_only_file,
-    )
 from lp.testing import TestCase
 from lp.testing.layers import DatabaseLayer
 
@@ -46,23 +41,3 @@
         self.assertEqual('not_launchpad', dbc.dbuser)
         dbc.reset()
         self.assertEqual('launchpad_main', dbc.dbuser)
-
-    def test_main_master_and_main_slave(self):
-        # DatabaseConfig provides two extra properties: main_master and
-        # main_slave, which return the value of either
-        # rw_main_master/rw_main_slave or ro_main_master/ro_main_slave,
-        # depending on whether or not we're in read-only mode.
-        dbc = DatabaseConfig()
-        self.assertFalse(read_only_file_exists())
-        self.assertEquals(dbc.rw_main_master, dbc.main_master)
-        self.assertEquals(dbc.rw_main_slave, dbc.main_slave)
-
-        touch_read_only_file()
-        try:
-            self.assertTrue(read_only_file_exists())
-            self.assertEquals(
-                dbc.ro_main_master, dbc.main_master)
-            self.assertEquals(
-                dbc.ro_main_slave, dbc.main_slave)
-        finally:
-            remove_read_only_file()

=== modified file 'lib/lp/services/database/doc/db-policy.txt'
--- lib/lp/services/database/doc/db-policy.txt	2012-02-02 10:26:54 +0000
+++ lib/lp/services/database/doc/db-policy.txt	2012-08-09 04:24:19 +0000
@@ -123,30 +123,3 @@
     >>> from lp.services.database.lpstorm import IMasterObject
     >>> IMasterObject(ro_janitor) is writable_janitor
     True
-
-Read-Only Mode
---------------
-
-During database outages, we run in read-only mode. In this mode, no
-matter what database policy is currently installed, explicit requests
-for a master store fail and the default store is always the slave.
-
-    >>> from lp.services.database.tests.readonly import read_only_mode
-    >>> from lp.services.webapp.dbpolicy import MasterDatabasePolicy
-    >>> from contextlib import nested
-
-    >>> with nested(read_only_mode(), MasterDatabasePolicy()):
-    ...     default_store = IStore(Person)
-    ...     IMasterStore.providedBy(default_store)
-    False
-
-    >>> with nested(read_only_mode(), MasterDatabasePolicy()):
-    ...     slave_store = ISlaveStore(Person)
-    ...     IMasterStore.providedBy(slave_store)
-    False
-
-    >>> with nested(read_only_mode(), MasterDatabasePolicy()):
-    ...     master_store = IMasterStore(Person)
-    Traceback (most recent call last):
-    ...
-    ReadOnlyModeDisallowedStore: ('main', 'master')

=== removed file 'lib/lp/services/database/readonly.py'
--- lib/lp/services/database/readonly.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/database/readonly.py	1970-01-01 00:00:00 +0000
@@ -1,83 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Helpers for running Launchpad in read-only mode.
-
-To switch an app server to read-only mode, all you need to do is create a file
-named read-only.txt in the root of the Launchpad tree.
-"""
-
-__metaclass__ = type
-__all__ = [
-    'is_read_only',
-    'read_only_file_exists',
-    'read_only_file_path',
-    ]
-
-import logging
-import os
-import threading
-
-from lazr.restful.utils import get_current_browser_request
-from zope.security.management import queryInteraction
-
-from lp.services.config import config
-
-
-read_only_file_path = os.path.join(config.root, 'read-only.txt')
-READ_ONLY_MODE_ANNOTATIONS_KEY = 'launchpad.read_only_mode'
-
-
-def read_only_file_exists():
-    """Does a read-only.txt file exists in the root of our tree?"""
-    return os.path.isfile(read_only_file_path)
-
-
-_lock = threading.Lock()
-_currently_in_read_only = False
-
-
-def is_read_only():
-    """Are we in read-only mode?
-
-    If called as part of the processing of a request, we'll look in the
-    request's annotations for a read-only key
-    (READ_ONLY_MODE_ANNOTATIONS_KEY), and if it exists we'll just return its
-    value.
-
-    If there's no request or the key doesn't exist, we check for the presence
-    of a read-only.txt file in the root of our tree, set the read-only key in
-    the request's annotations (when there is a request), update
-    _currently_in_read_only (in case it changed, also logging the change)
-    and return it.
-    """
-    # pylint: disable-msg=W0603
-    global _currently_in_read_only
-    request = None
-    # XXX: salgado, 2010-01-14, bug=507447: Only call
-    # get_current_browser_request() when we have an interaction, or else
-    # it will raise an AttributeError.
-    if queryInteraction() is not None:
-        request = get_current_browser_request()
-    if request is not None:
-        if READ_ONLY_MODE_ANNOTATIONS_KEY in request.annotations:
-            return request.annotations[READ_ONLY_MODE_ANNOTATIONS_KEY]
-
-    read_only = read_only_file_exists()
-    if request is not None:
-        request.annotations[READ_ONLY_MODE_ANNOTATIONS_KEY] = read_only
-
-    log_change = False
-    with _lock:
-        if _currently_in_read_only != read_only:
-            _currently_in_read_only = read_only
-            log_change = True
-
-    if log_change:
-        logging.warning(
-            'Read-only mode change detected; now read-only is %s' % read_only)
-
-    return read_only
-
-
-_currently_in_read_only = is_read_only()

=== removed file 'lib/lp/services/database/tests/readonly.py'
--- lib/lp/services/database/tests/readonly.py	2011-12-24 15:54:55 +0000
+++ lib/lp/services/database/tests/readonly.py	1970-01-01 00:00:00 +0000
@@ -1,67 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Helpers for creating and removing a read-only.txt in the root of our tree.
-"""
-
-__metaclass__ = type
-__all__ = [
-    'touch_read_only_file',
-    'read_only_mode',
-    'remove_read_only_file',
-    ]
-
-from contextlib import contextmanager
-import os
-
-from lazr.restful.utils import get_current_browser_request
-
-from lp.services.database.readonly import (
-    is_read_only,
-    read_only_file_exists,
-    read_only_file_path,
-    READ_ONLY_MODE_ANNOTATIONS_KEY,
-    )
-
-
-def touch_read_only_file():
-    """Create an empty file named read-only.txt under the root of the tree.
-
-    This function must not be called if a file with that name already exists.
-    """
-    assert not read_only_file_exists(), (
-        "This function must not be called when a read-only.txt file "
-        "already exists.")
-    f = open(read_only_file_path, 'w')
-    f.close()
-    # Assert that the switch succeeded and make sure the mode change is
-    # logged.
-    assert is_read_only(), "Switching to read-only failed."
-
-
-def remove_read_only_file(assert_mode_switch=True):
-    """Remove the file named read-only.txt from the root of the tree.
-
-    May also assert that the mode switch actually happened (i.e. not
-    is_read_only()). This assertion has to be conditional because some tests
-    will use this during the processing of a request, when a mode change can't
-    happen (i.e. is_read_only() will still return True during that request's
-    processing, even though the read-only.txt file has been removed).
-    """
-    os.remove(read_only_file_path)
-    if assert_mode_switch:
-        # Assert that the switch succeeded and make sure the mode change is
-        # logged.
-        assert not is_read_only(), "Switching to read-write failed."
-
-
-@contextmanager
-def read_only_mode(flag=True):
-    request = get_current_browser_request()
-    current = request.annotations[READ_ONLY_MODE_ANNOTATIONS_KEY]
-    request.annotations[READ_ONLY_MODE_ANNOTATIONS_KEY] = flag
-    try:
-        assert is_read_only() == flag, 'Failed to set read-only mode'
-        yield
-    finally:
-        request.annotations[READ_ONLY_MODE_ANNOTATIONS_KEY] = current

=== removed file 'lib/lp/services/database/tests/test_readonly.py'
--- lib/lp/services/database/tests/test_readonly.py	2012-03-14 18:38:28 +0000
+++ lib/lp/services/database/tests/test_readonly.py	1970-01-01 00:00:00 +0000
@@ -1,88 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-__metaclass__ = type
-
-from lazr.restful.utils import get_current_browser_request
-from zope.security.management import endInteraction
-
-from lp.services.database.readonly import (
-    is_read_only,
-    read_only_file_exists,
-    READ_ONLY_MODE_ANNOTATIONS_KEY,
-    )
-from lp.services.database.tests.readonly import (
-    remove_read_only_file,
-    touch_read_only_file,
-    )
-from lp.testing import (
-    ANONYMOUS,
-    login,
-    logout,
-    TestCase,
-    )
-from lp.testing.layers import FunctionalLayer
-
-
-class TestReadOnlyModeDetection(TestCase):
-
-    def test_read_only_file_exists(self):
-        # By default we run in read-write mode.
-        self.assertFalse(read_only_file_exists())
-
-        # When a file named 'read-only.txt' exists under the root of the tree,
-        # we run in read-only mode.
-        touch_read_only_file()
-        try:
-            self.assertTrue(read_only_file_exists())
-        finally:
-            remove_read_only_file()
-
-        # Once the file is removed, we're back into read-write mode.
-        self.assertFalse(read_only_file_exists())
-
-
-class Test_is_read_only(TestCase):
-    layer = FunctionalLayer
-
-    def tearDown(self):
-        # Safety net just in case a test leaves the read-only.txt file behind.
-        if read_only_file_exists():
-            remove_read_only_file()
-        endInteraction()
-        super(Test_is_read_only, self).tearDown()
-
-    def test_is_read_only(self):
-        # By default we run in read-write mode.
-        logout()
-        self.assertFalse(is_read_only())
-
-        # When a file named 'read-only.txt' exists under the root of the tree,
-        # we run in read-only mode.
-        touch_read_only_file()
-        try:
-            self.assertTrue(is_read_only())
-        finally:
-            remove_read_only_file()
-
-    def test_caching_in_request(self):
-        # When called as part of a request processing, is_read_only() will
-        # stash the read-only flag in the request's annotations.
-        login(ANONYMOUS)
-        request = get_current_browser_request()
-        self.assertIs(
-            None,
-            request.annotations.get(READ_ONLY_MODE_ANNOTATIONS_KEY))
-        self.assertFalse(is_read_only())
-        self.assertFalse(
-            request.annotations.get(READ_ONLY_MODE_ANNOTATIONS_KEY))
-
-    def test_cached_value_takes_precedence(self):
-        # Once the request has the read-only flag, we don't check for the
-        # presence of the read-only.txt file anymore, so it could be removed
-        # and the request would still be in read-only mode.
-        login(ANONYMOUS)
-        request = get_current_browser_request()
-        request.annotations[READ_ONLY_MODE_ANNOTATIONS_KEY] = True
-        self.assertTrue(is_read_only())
-        self.assertFalse(read_only_file_exists())

=== modified file 'lib/lp/services/feeds/configure.zcml'
--- lib/lp/services/feeds/configure.zcml	2011-12-30 07:32:58 +0000
+++ lib/lp/services/feeds/configure.zcml	2012-08-09 04:24:19 +0000
@@ -46,6 +46,7 @@
         name="index.html"
         permission="zope.Public"
         class="lp.services.webapp.login.FeedsUnauthorizedView"
+        template="../../app/templates/launchpad-forbidden.pt"
         attribute="__call__"
         layer="lp.layers.FeedsLayer"
         />

=== modified file 'lib/lp/services/webapp/adapter.py'
--- lib/lp/services/webapp/adapter.py	2012-06-29 08:40:05 +0000
+++ lib/lp/services/webapp/adapter.py	2012-08-09 04:24:19 +0000
@@ -21,7 +21,6 @@
     get_current_browser_request,
     safe_hasattr,
     )
-import psycopg2
 from psycopg2.extensions import (
     ISOLATION_LEVEL_AUTOCOMMIT,
     ISOLATION_LEVEL_READ_COMMITTED,
@@ -36,7 +35,6 @@
     )
 from storm.databases.postgres import (
     Postgres,
-    PostgresConnection,
     PostgresTimeoutTracer,
     )
 from storm.exceptions import TimeoutError
@@ -65,7 +63,6 @@
     IMasterStore,
     )
 from lp.services.database.postgresql import ConnectionString
-from lp.services.database.readonly import is_read_only
 from lp.services.log.loglevels import DEBUG2
 from lp.services.stacktrace import (
     extract_stack,
@@ -84,8 +81,6 @@
     IStoreSelector,
     MAIN_STORE,
     MASTER_FLAVOR,
-    ReadOnlyModeDisallowedStore,
-    ReadOnlyModeViolation,
     SLAVE_FLAVOR,
     )
 from lp.services.webapp.opstats import OpStats
@@ -473,23 +468,6 @@
     }
 
 
-class ReadOnlyModeConnection(PostgresConnection):
-    """storm.database.Connection for read-only mode Launchpad."""
-
-    def execute(self, statement, params=None, noresult=False):
-        """See storm.database.Connection."""
-        try:
-            return super(ReadOnlyModeConnection, self).execute(
-                statement, params, noresult)
-        except psycopg2.InternalError as exception:
-            # Error 25006 is 'ERROR:  transaction is read-only'. This
-            # is raised when an attempt is made to make changes when
-            # the connection has been put in read-only mode.
-            if exception.pgcode == '25006':
-                raise ReadOnlyModeViolation(None, sys.exc_info()[2])
-            raise
-
-
 class LaunchpadDatabase(Postgres):
 
     _dsn_user_re = re.compile('user=[^ ]*')
@@ -572,17 +550,6 @@
             flavor, raw_connection.get_backend_pid(), dbuser, self._isolation)
         return raw_connection
 
-    @property
-    def connection_factory(self):
-        """Return the correct connection factory for the current mode.
-
-        If we are running in read-only mode, returns a
-        ReadOnlyModeConnection. Otherwise it returns the Storm default.
-        """
-        if is_read_only():
-            return ReadOnlyModeConnection
-        return super(LaunchpadDatabase, self).connection_factory
-
 
 class LaunchpadSessionDatabase(Postgres):
 
@@ -828,20 +795,6 @@
     @staticmethod
     def get(name, flavor):
         """See `IStoreSelector`."""
-        if is_read_only():
-            # If we are in read-only mode, override the default to the
-            # slave no matter what the existing policy says (it might
-            # work), and raise an exception if the master was explicitly
-            # requested. Most of the time, this doesn't matter as when
-            # we are in read-only mode we have a suitable database
-            # policy installed. However, code can override the policy so
-            # we still need to catch disallowed requests here.
-            if flavor == DEFAULT_FLAVOR:
-                flavor = SLAVE_FLAVOR
-            elif flavor == MASTER_FLAVOR:
-                raise ReadOnlyModeDisallowedStore(name, flavor)
-            else:
-                pass
         db_policy = StoreSelector.get_current()
         if db_policy is None:
             db_policy = MasterDatabasePolicy(None)

=== modified file 'lib/lp/services/webapp/authorization.py'
--- lib/lp/services/webapp/authorization.py	2012-03-31 11:32:15 +0000
+++ lib/lp/services/webapp/authorization.py	2012-08-09 04:24:19 +0000
@@ -40,7 +40,6 @@
 
 from lp.app.interfaces.security import IAuthorization
 from lp.registry.interfaces.role import IPersonRoles
-from lp.services.database.readonly import is_read_only
 from lp.services.database.sqlbase import block_implicit_flushes
 from lp.services.privacy.interfaces import IObjectPrivacy
 from lp.services.webapp.canonicalurl import nearest_adapter
@@ -144,15 +143,6 @@
           after the permission, use that to check the permission.
         - Otherwise, deny.
         """
-        # Shortcut in read-only mode. We have to do this now to avoid
-        # accidentally using cached results. This will be important when
-        # Launchpad automatically fails over to read-only mode when the
-        # master database is unavailable.
-        if is_read_only():
-            lp_permission = getUtility(ILaunchpadPermission, permission)
-            if lp_permission.access_level != "read":
-                return False
-
         # If we have a view, get its context and use that to get an
         # authorization adapter.
         if IView.providedBy(object):

=== modified file 'lib/lp/services/webapp/dbpolicy.py'
--- lib/lp/services/webapp/dbpolicy.py	2012-06-20 18:48:02 +0000
+++ lib/lp/services/webapp/dbpolicy.py	2012-08-09 04:24:19 +0000
@@ -9,7 +9,6 @@
     'DatabaseBlockedPolicy',
     'LaunchpadDatabasePolicy',
     'MasterDatabasePolicy',
-    'ReadOnlyLaunchpadDatabasePolicy',
     'SlaveDatabasePolicy',
     'SlaveOnlyDatabasePolicy',
     ]
@@ -45,7 +44,6 @@
     IMasterStore,
     ISlaveStore,
     )
-from lp.services.database.readonly import is_read_only
 from lp.services.database.sqlbase import StupidCache
 from lp.services.webapp import LaunchpadView
 from lp.services.webapp.interfaces import (
@@ -55,7 +53,6 @@
     IStoreSelector,
     MAIN_STORE,
     MASTER_FLAVOR,
-    ReadOnlyModeDisallowedStore,
     SLAVE_FLAVOR,
     )
 
@@ -195,8 +192,6 @@
     # of test requests in our automated tests.
     if request.get('PATH_INFO') in [u'/+opstats', u'/+haproxy']:
         return DatabaseBlockedPolicy(request)
-    elif is_read_only():
-        return ReadOnlyLaunchpadDatabasePolicy(request)
     else:
         return LaunchpadDatabasePolicy(request)
 
@@ -357,41 +352,15 @@
 def WebServiceDatabasePolicyFactory(request):
     """Return the Launchpad IDatabasePolicy for the current appserver state.
     """
-    if is_read_only():
-        return ReadOnlyLaunchpadDatabasePolicy(request)
-    else:
-        # If a session cookie was sent with the request, use the
-        # standard Launchpad database policy for load balancing to
-        # the slave databases. The javascript web service libraries
-        # send the session cookie for authenticated users.
-        cookie_name = getUtility(IClientIdManager).namespace
-        if cookie_name in request.cookies:
-            return LaunchpadDatabasePolicy(request)
-        # Otherwise, use the master only web service database policy.
-        return MasterDatabasePolicy(request)
-
-
-class ReadOnlyLaunchpadDatabasePolicy(BaseDatabasePolicy):
-    """Policy for Launchpad web requests when running in read-only mode.
-
-    Access to all master Stores is blocked.
-    """
-
-    def getStore(self, name, flavor):
-        """See `IDatabasePolicy`.
-
-        Access to all master Stores is blocked. The default Store is
-        the slave.
-
-        Note that we even have to block access to the authdb master
-        Store, as it allows access to tables replicated from the
-        lpmain replication set. These tables will be locked during
-        a lpmain replication set database upgrade.
-        """
-        if flavor == MASTER_FLAVOR:
-            raise ReadOnlyModeDisallowedStore(name, flavor)
-        return super(ReadOnlyLaunchpadDatabasePolicy, self).getStore(
-            name, SLAVE_FLAVOR)
+    # If a session cookie was sent with the request, use the
+    # standard Launchpad database policy for load balancing to
+    # the slave databases. The javascript web service libraries
+    # send the session cookie for authenticated users.
+    cookie_name = getUtility(IClientIdManager).namespace
+    if cookie_name in request.cookies:
+        return LaunchpadDatabasePolicy(request)
+    # Otherwise, use the master only web service database policy.
+    return MasterDatabasePolicy(request)
 
 
 class WhichDbView(LaunchpadView):

=== modified file 'lib/lp/services/webapp/errorlog.py'
--- lib/lp/services/webapp/errorlog.py	2012-05-15 07:15:17 +0000
+++ lib/lp/services/webapp/errorlog.py	2012-08-09 04:24:19 +0000
@@ -294,9 +294,7 @@
 class ErrorReportingUtility:
     implements(IErrorReportingUtility)
 
-    _ignored_exceptions = set([
-        'ReadOnlyModeDisallowedStore', 'ReadOnlyModeViolation',
-        'TranslationUnavailable', 'NoReferrerError'])
+    _ignored_exceptions = set(['TranslationUnavailable', 'NoReferrerError'])
     _ignored_exceptions_for_offsite_referer = set([
         'GoneError', 'InvalidBatchSizeError', 'NotFound'])
     _default_config_section = 'error_reports'

=== modified file 'lib/lp/services/webapp/interfaces.py'
--- lib/lp/services/webapp/interfaces.py	2012-02-22 18:51:23 +0000
+++ lib/lp/services/webapp/interfaces.py	2012-08-09 04:24:19 +0000
@@ -805,21 +805,6 @@
     """
 
 
-class ReadOnlyModeViolation(Exception):
-    """An attempt was made to write to a slave Store in read-only mode.
-
-    This can happen in legacy code where writes are being made to an
-    object retrieved from the default Store rather than casting the
-    object to a writable version using IMasterObject(obj).
-    """
-
-
-class ReadOnlyModeDisallowedStore(DisallowedStore, ReadOnlyModeViolation):
-    """A request was made to access a Store that cannot be granted
-    because we are running in read-only mode.
-    """
-
-
 class IStoreSelector(Interface):
     """Get a Storm store with a desired flavor.
 

=== modified file 'lib/lp/services/webapp/login.py'
--- lib/lp/services/webapp/login.py	2012-08-08 14:27:28 +0000
+++ lib/lp/services/webapp/login.py	2012-08-09 04:24:19 +0000
@@ -45,7 +45,6 @@
     TeamEmailAddressError,
     )
 from lp.services.config import config
-from lp.services.database.readonly import is_read_only
 from lp.services.identity.interfaces.account import AccountSuspendedError
 from lp.services.openid.interfaces.openidconsumer import IOpenIDConsumerStore
 from lp.services.propertycache import cachedproperty
@@ -59,7 +58,6 @@
     IPlacelessLoginSource,
     LoggedOutEvent,
     )
-from lp.services.webapp.metazcml import ILaunchpadPermission
 from lp.services.webapp.publisher import LaunchpadView
 from lp.services.webapp.url import urlappend
 from lp.services.webapp.vhosts import allvhosts
@@ -68,33 +66,9 @@
 class UnauthorizedView(SystemErrorView):
 
     response_code = None
-
-    forbidden_page = ViewPageTemplateFile(
-        '../../../lp/app/templates/launchpad-forbidden.pt')
-
-    read_only_page = ViewPageTemplateFile(
-        '../../../lp/app/templates/launchpad-readonlyfailure.pt')
-
-    def page_title(self):
-        if is_read_only():
-            return super(UnauthorizedView, self).page_title
-        else:
-            return 'Forbidden'
+    page_title = 'Forbidden'
 
     def __call__(self):
-        # In read only mode, Unauthorized exceptions get raised by the
-        # security policy when write permissions are requested. We need
-        # to render the read-only failure screen so the user knows their
-        # request failed for operational reasons rather than a genuine
-        # permission problem.
-        if is_read_only():
-            # Our context is an Unauthorized exception, which acts like
-            # a tuple containing (object, attribute_requested, permission).
-            lp_permission = getUtility(ILaunchpadPermission, self.context[2])
-            if lp_permission.access_level != "read":
-                self.request.response.setStatus(503)  # Service Unavailable
-                return self.read_only_page()
-
         if IUnauthenticatedPrincipal.providedBy(self.request.principal):
             if 'loggingout' in self.request.form:
                 target = '%s?loggingout=1' % self.request.URL[-2]
@@ -133,7 +107,7 @@
             return ''
         else:
             self.request.response.setStatus(403)  # Forbidden
-            return self.forbidden_page()
+            return self.template()
 
     def getRedirectURL(self, current_url, query_string):
         """Get the URL to redirect to.
@@ -569,4 +543,4 @@
         assert IUnauthenticatedPrincipal.providedBy(self.request.principal), (
             "Feeds user should always be anonymous.")
         self.request.response.setStatus(403)  # Forbidden
-        return self.forbidden_page()
+        return self.template()

=== modified file 'lib/lp/services/webapp/publication.py'
--- lib/lp/services/webapp/publication.py	2012-01-04 05:07:53 +0000
+++ lib/lp/services/webapp/publication.py	2012-08-09 04:24:19 +0000
@@ -64,7 +64,6 @@
     )
 from lp.services import features
 from lp.services.config import config
-from lp.services.database.readonly import is_read_only
 from lp.services.features.flags import NullFeatureController
 from lp.services.oauth.interfaces import IOAuthSignedRequest
 from lp.services.osutils import open_for_writing
@@ -74,7 +73,6 @@
     FinishReadOnlyRequestEvent,
     IDatabasePolicy,
     ILaunchpadRoot,
-    INotificationResponse,
     IOpenLaunchBag,
     IPlacelessAuthUtility,
     IPrimaryContext,
@@ -84,7 +82,6 @@
     OffsiteFormPostError,
     StartRequestEvent,
     )
-from lp.services.webapp.menu import structured
 from lp.services.webapp.opstats import OpStats
 from lp.services.webapp.vhosts import allvhosts
 
@@ -267,32 +264,9 @@
 
         transaction.begin()
 
-        db_policy = IDatabasePolicy(request)
-
-        # If we have switched to or from read-only mode, we need to
-        # disconnect all Stores for this thread. We don't want the
-        # appserver to leave dangling connections as this will interfere
-        # with database maintenance.
-        # We don't disconnect Stores for threads currently handling
-        # requests. That would generate unreproducable OOPSes. This
-        # isn't a problem, as our requests should complete soon or
-        # timeout. Unfortunately, there is no way to disconnect Stores
-        # for idle threads. This means connections are left dangling
-        # until the appserver has processed as many requests as there
-        # are worker threads. We will be able to handle this better
-        # when we have a connection pool.
-        was_read_only = getattr(self.thread_locals, 'was_read_only', None)
-        if was_read_only is not None and was_read_only != is_read_only():
-            zstorm = getUtility(IZStorm)
-            for name, store in list(zstorm.iterstores()):
-                zstorm.remove(store)
-                store.close()
-        # is_read_only() is cached for the entire request, so there
-        # is no race condition here.
-        self.thread_locals.was_read_only = is_read_only()
-
         # Now we are logged in, install the correct IDatabasePolicy for
         # this request.
+        db_policy = IDatabasePolicy(request)
         getUtility(IStoreSelector).push(db_policy)
 
         getUtility(IOpenLaunchBag).clear()
@@ -308,21 +282,6 @@
         request.setPrincipal(principal)
         self.maybeRestrictToTeam(request)
         maybe_block_offsite_form_post(request)
-        self.maybeNotifyReadOnlyMode(request)
-
-    def maybeNotifyReadOnlyMode(self, request):
-        """Hook to notify about read-only mode."""
-        if is_read_only():
-            notification_response = INotificationResponse(request, None)
-            if notification_response is not None:
-                notification_response.addWarningNotification(
-                    structured("""
-                        Launchpad is undergoing maintenance and is in
-                        read-only mode. <i>You cannot make any
-                        changes.</i> You can find more information on the
-                        <a href="http://identi.ca/launchpadstatus";>Launchpad
-                        system status</a> page.
-                        """))
 
     def getPrincipal(self, request):
         """Return the authenticated principal for this request.

=== modified file 'lib/lp/services/webapp/tests/test_dbpolicy.py'
--- lib/lp/services/webapp/tests/test_dbpolicy.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/webapp/tests/test_dbpolicy.py	2012-08-09 04:24:19 +0000
@@ -30,15 +30,10 @@
     IMasterStore,
     ISlaveStore,
     )
-from lp.services.database.tests.readonly import (
-    remove_read_only_file,
-    touch_read_only_file,
-    )
 from lp.services.webapp.dbpolicy import (
     BaseDatabasePolicy,
     LaunchpadDatabasePolicy,
     MasterDatabasePolicy,
-    ReadOnlyLaunchpadDatabasePolicy,
     SlaveDatabasePolicy,
     SlaveOnlyDatabasePolicy,
     )
@@ -50,7 +45,6 @@
     IStoreSelector,
     MAIN_STORE,
     MASTER_FLAVOR,
-    ReadOnlyModeDisallowedStore,
     SLAVE_FLAVOR,
     )
 from lp.services.webapp.servers import LaunchpadTestRequest
@@ -230,63 +224,9 @@
         finally:
             endInteraction()
 
-    def test_WebServiceRequest_uses_ReadOnlyDatabasePolicy(self):
-        """WebService requests should use the read only database
-        policy in read only mode.
-        """
-        touch_read_only_file()
-        try:
-            api_prefix = getUtility(
-                IWebServiceConfiguration).active_versions[0]
-            server_url = 'http://api.launchpad.dev/%s' % api_prefix
-            request = LaunchpadTestRequest(SERVER_URL=server_url)
-            setFirstLayer(request, WebServiceLayer)
-            policy = IDatabasePolicy(request)
-            self.assertIsInstance(policy, ReadOnlyLaunchpadDatabasePolicy)
-        finally:
-            remove_read_only_file()
-
-    def test_read_only_mode_uses_ReadOnlyLaunchpadDatabasePolicy(self):
-        touch_read_only_file()
-        try:
-            request = LaunchpadTestRequest(
-                SERVER_URL='http://launchpad.dev')
-            policy = IDatabasePolicy(request)
-            self.assertIsInstance(policy, ReadOnlyLaunchpadDatabasePolicy)
-        finally:
-            remove_read_only_file()
-
     def test_other_request_uses_LaunchpadDatabasePolicy(self):
         """By default, requests should use the LaunchpadDatabasePolicy."""
         server_url = 'http://launchpad.dev/'
         request = LaunchpadTestRequest(SERVER_URL=server_url)
         policy = IDatabasePolicy(request)
         self.assertIsInstance(policy, LaunchpadDatabasePolicy)
-
-
-class ReadOnlyLaunchpadDatabasePolicyTestCase(BaseDatabasePolicyTestCase):
-    """Tests for the `ReadOnlyModeLaunchpadDatabasePolicy`"""
-
-    def setUp(self):
-        self.policy = ReadOnlyLaunchpadDatabasePolicy()
-        super(ReadOnlyLaunchpadDatabasePolicyTestCase, self).setUp()
-
-    def test_defaults(self):
-        # default Store is the slave.
-        for store in ALL_STORES:
-            self.assertProvides(
-                getUtility(IStoreSelector).get(store, DEFAULT_FLAVOR),
-                ISlaveStore)
-
-    def test_slave_allowed(self):
-        for store in ALL_STORES:
-            self.assertProvides(
-                getUtility(IStoreSelector).get(store, SLAVE_FLAVOR),
-                ISlaveStore)
-
-    def test_master_disallowed(self):
-        store_selector = getUtility(IStoreSelector)
-        for store in ALL_STORES:
-            self.assertRaises(
-                ReadOnlyModeDisallowedStore,
-                store_selector.get, store, MASTER_FLAVOR)

=== modified file 'lib/lp/services/webapp/tests/test_publication.py'
--- lib/lp/services/webapp/tests/test_publication.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/webapp/tests/test_publication.py	2012-08-09 04:24:19 +0000
@@ -5,7 +5,6 @@
 
 __metaclass__ = type
 
-import logging
 import sys
 
 from contrib.oauth import (
@@ -17,7 +16,6 @@
     STATE_RECONNECT,
     )
 from storm.exceptions import DisconnectionError
-from storm.zope.interfaces import IZStorm
 from zope.component import getUtility
 from zope.interface import directlyProvides
 from zope.publisher.interfaces import (
@@ -25,13 +23,7 @@
     Retry,
     )
 
-from lp.services.config import dbconfig
 from lp.services.database.lpstorm import IMasterStore
-from lp.services.database.readonly import is_read_only
-from lp.services.database.tests.readonly import (
-    remove_read_only_file,
-    touch_read_only_file,
-    )
 from lp.services.identity.model.emailaddress import EmailAddress
 from lp.services.oauth.interfaces import (
     IOAuthConsumerSet,
@@ -39,13 +31,9 @@
     )
 import lp.services.webapp.adapter as dbadapter
 from lp.services.webapp.interfaces import (
-    IStoreSelector,
-    MAIN_STORE,
-    MASTER_FLAVOR,
     NoReferrerError,
     OAuthPermission,
     OffsiteFormPostError,
-    SLAVE_FLAVOR,
     )
 from lp.services.webapp.publication import (
     is_browser,
@@ -64,10 +52,7 @@
     TestCase,
     TestCaseWithFactory,
     )
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    FunctionalLayer,
-    )
+from lp.testing.layers import DatabaseFunctionalLayer
 
 
 class TestLaunchpadBrowserPublication(TestCase):
@@ -95,137 +80,6 @@
         self.assertEquals(request.traversed_objects, [obj1])
 
 
-class TestReadOnlyModeSwitches(TestCase):
-    # At the beginning of every request (in publication.beforeTraversal()), we
-    # check to see if we've changed from/to read-only/read-write and if there
-    # was a change we remove the main_master/slave stores from ZStorm, forcing
-    # them to be recreated the next time they're needed, thus causing them to
-    # point to the correct databases.
-    layer = DatabaseFunctionalLayer
-
-    def tearDown(self):
-        TestCase.tearDown(self)
-        # If a DB policy was installed (e.g. by publication.beforeTraversal),
-        # uninstall it.
-        try:
-            getUtility(IStoreSelector).pop()
-        except IndexError:
-            pass
-        # Cleanup needed so that further tests can start processing other
-        # requests (e.g. calling beforeTraversal).
-        self.publication.endRequest(self.request, None)
-        # Force pending mode switches to actually happen and get logged so
-        # that we don't interfere with other tests.
-        assert not is_read_only(), (
-            "A test failed to clean things up properly, leaving the app "
-            "in read-only mode.")
-
-    def setUp(self):
-        TestCase.setUp(self)
-        # Get the main_master/slave stores just to make sure they're added to
-        # ZStorm.
-        master = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
-        slave = getUtility(IStoreSelector).get(MAIN_STORE, SLAVE_FLAVOR)
-        self.master_connection = master._connection
-        self.slave_connection = slave._connection
-        self.zstorm = getUtility(IZStorm)
-        self.publication = LaunchpadBrowserPublication(None)
-        # Run through once to initialize. beforeTraversal will never
-        # disconnect Stores the first run through because there is no
-        # need.
-        request = LaunchpadTestRequest()
-        self.publication.beforeTraversal(request)
-        self.publication.endRequest(request, None)
-        getUtility(IStoreSelector).pop()
-
-        self.request = LaunchpadTestRequest()
-
-    @property
-    def zstorm_stores(self):
-        return [name for (name, store) in self.zstorm.iterstores()]
-
-    def test_no_mode_changes(self):
-        # Make sure the master/slave stores are present in zstorm.
-        self.assertIn('main-master', self.zstorm_stores)
-        self.assertIn('main-slave', self.zstorm_stores)
-
-        self.publication.beforeTraversal(self.request)
-
-        # Since the mode didn't change, the stores were left in zstorm.
-        self.assertIn('main-master', self.zstorm_stores)
-        self.assertIn('main-slave', self.zstorm_stores)
-
-        # With the store's connection being the same as before.
-        master = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
-        self.assertIs(self.master_connection, master._connection)
-
-        # And they still point to the read-write databases.
-        self.assertEquals(
-            dbconfig.rw_main_master.strip(),
-            # XXX: 2009-01-12, salgado, bug=506536: We shouldn't need to go
-            # through private attributes to get to the store's database.
-            master._connection._database.dsn_without_user.strip())
-
-    def test_changing_modes(self):
-        # Make sure the master/slave stores are present in zstorm.
-        self.assertIn('main-master', self.zstorm_stores)
-        self.assertIn('main-slave', self.zstorm_stores)
-
-        try:
-            touch_read_only_file()
-            self.publication.beforeTraversal(self.request)
-        finally:
-            # Tell remove_read_only_file() to not assert that the mode switch
-            # actually happened, as we know it won't happen until this request
-            # is finished.
-            remove_read_only_file(assert_mode_switch=False)
-
-        # Here the mode has changed to read-only, so the stores were removed
-        # from zstorm.
-        self.assertNotIn('main-master', self.zstorm_stores)
-        self.assertNotIn('main-slave', self.zstorm_stores)
-
-        # If they're needed again, they'll be re-created by ZStorm, and when
-        # that happens they will point to the read-only databases.
-        master = getUtility(IStoreSelector).get(MAIN_STORE, SLAVE_FLAVOR)
-        self.assertEquals(
-            dbconfig.ro_main_master.strip(),
-            # XXX: 2009-01-12, salgado, bug=506536: We shouldn't need to go
-            # through private attributes to get to the store's database.
-            master._connection._database.dsn_without_user.strip())
-
-
-class TestReadOnlyNotifications(TestCase):
-    """Tests for `LaunchpadBrowserPublication.maybeNotifyReadOnlyMode`."""
-
-    layer = FunctionalLayer
-
-    def setUp(self):
-        TestCase.setUp(self)
-        touch_read_only_file()
-        self.addCleanup(remove_read_only_file, assert_mode_switch=False)
-
-    def test_notification(self):
-        # In read-only mode, maybeNotifyReadOnlyMode adds a warning that
-        # changes cannot be made to every request that supports notifications.
-        publication = LaunchpadBrowserPublication(None)
-        request = LaunchpadTestRequest()
-        publication.maybeNotifyReadOnlyMode(request)
-        self.assertEqual(1, len(request.notifications))
-        notification = request.notifications[0]
-        self.assertEqual(logging.WARNING, notification.level)
-        self.assertTrue('read-only mode' in notification.message)
-
-    def test_notification_xmlrpc(self):
-        # Even in read-only mode, maybeNotifyReadOnlyMode doesn't try to add a
-        # notification to a request that doesn't support notifications.
-        from lp.services.webapp.servers import PublicXMLRPCRequest
-        publication = LaunchpadBrowserPublication(None)
-        request = PublicXMLRPCRequest(None, {})
-        # This is just assertNotRaises
-        publication.maybeNotifyReadOnlyMode(request)
-
-
 class TestWebServicePublication(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 

=== modified file 'lib/lp/translations/browser/serieslanguage.py'
--- lib/lp/translations/browser/serieslanguage.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/browser/serieslanguage.py	2012-08-09 04:24:19 +0000
@@ -15,7 +15,6 @@
 from lp.app.browser.tales import PersonFormatterAPI
 from lp.registry.model.sourcepackagename import SourcePackageName
 from lp.services.database.bulk import load_related
-from lp.services.database.readonly import is_read_only
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import LaunchpadView
 from lp.services.webapp.batching import BatchNavigator
@@ -85,12 +84,6 @@
     @property
     def access_level_description(self):
         """Must not be called when there's no translation group."""
-
-        if is_read_only():
-            return (
-                "No work can be done on these translations while Launchpad "
-                "is in read-only mode.")
-
         if self.user is None:
             return ("You are not logged in. Please log in to work "
                     "on translations.")

=== modified file 'lib/lp/translations/browser/tests/test_distroserieslanguage_views.py'
--- lib/lp/translations/browser/tests/test_distroserieslanguage_views.py	2012-01-01 02:58:52 +0000
+++ lib/lp/translations/browser/tests/test_distroserieslanguage_views.py	2012-08-09 04:24:19 +0000
@@ -3,7 +3,6 @@
 
 __metaclass__ = type
 
-from lazr.restful.utils import get_current_browser_request
 from testtools.matchers import Equals
 import transaction
 from zope.component import getUtility
@@ -40,11 +39,6 @@
         self.view = DistroSeriesLanguageView(
             self.dsl, LaunchpadTestRequest())
 
-    def _simulateReadOnlyMode(self):
-        """Pretend to be in read-only mode for this test."""
-        request = get_current_browser_request()
-        request.annotations['launchpad.read_only_mode'] = True
-
     def test_empty_view(self):
         self.assertEquals(self.view.translation_group, None)
         self.assertEquals(self.view.translation_team, None)
@@ -78,13 +72,6 @@
         self.view.initialize()
         self.assertEquals(self.view.translation_team, translator)
 
-    def test_access_level_description_handles_readonly(self):
-        self._simulateReadOnlyMode()
-        notice = (
-            "No work can be done on these translations while Launchpad "
-            "is in read-only mode.")
-        self.assertEqual(notice, self.view.access_level_description)
-
     def test_sourcepackagenames_bulk_loaded(self):
         # SourcePackageName records referenced by POTemplates
         # are bulk loaded. Accessing the sourcepackagename attribute

=== modified file 'lib/lp/translations/browser/translationmessage.py'
--- lib/lp/translations/browser/translationmessage.py	2012-06-29 08:40:05 +0000
+++ lib/lp/translations/browser/translationmessage.py	2012-08-09 04:24:19 +0000
@@ -39,7 +39,6 @@
 from zope.schema.vocabulary import getVocabularyRegistry
 
 from lp.app.errors import UnexpectedFormData
-from lp.services.database.readonly import is_read_only
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
     ApplicationMenu,
@@ -382,11 +381,6 @@
             principle the user should not have been given the option to
             submit the current request.
         """
-        if is_read_only():
-            raise UnexpectedFormData(
-                "Launchpad is currently in read-only mode for maintenance.  "
-                "Please try again later.")
-
         if self.user is None:
             raise UnexpectedFormData("You are not logged in.")
 

=== modified file 'lib/lp/translations/model/pofile.py'
--- lib/lp/translations/model/pofile.py	2012-06-29 08:40:05 +0000
+++ lib/lp/translations/model/pofile.py	2012-08-09 04:24:19 +0000
@@ -52,7 +52,6 @@
 from lp.services.database.constants import UTC_NOW
 from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.lpstorm import IStore
-from lp.services.database.readonly import is_read_only
 from lp.services.database.sqlbase import (
     flush_database_updates,
     quote,
@@ -153,17 +152,11 @@
 
     def canEditTranslations(self, person):
         """See `IPOFile`."""
-        if is_read_only():
-            # Nothing can be edited in read-only mode.
-            return False
         policy = self.potemplate.getTranslationPolicy()
         return policy.allowsTranslationEdits(person, self.language)
 
     def canAddSuggestions(self, person):
         """See `IPOFile`."""
-        if is_read_only():
-            # No data can be entered in read-only mode.
-            return False
         policy = self.potemplate.getTranslationPolicy()
         return policy.allowsTranslationSuggestions(person, self.language)
 


Follow ups