def main(self): """See `LaunchpadScript`.""" self.validateOptions() self.logOptions() for distribution in self.findDistros(): allowed_suites = self.findAllowedSuites(distribution) for archive in self.getTargetArchives(distribution): set_request_started( request_statements=LimitedList(10000), txn=self.txn, enable_timeout=False) try: if archive.status == ArchiveStatus.DELETING: publisher = self.getPublisher( distribution, archive, allowed_suites) work_done = self.deleteArchive(archive, publisher) elif archive.can_be_published: publisher = self.getPublisher( distribution, archive, allowed_suites) self.publishArchive(archive, publisher) work_done = True else: work_done = False finally: clear_request_started() if work_done: self.txn.commit() self.logger.debug("Ciao")
def processDeathRow(self, archive): """Process death-row for the given archive. It handles the current DB transaction according to the results of the operation just executed, i.e, commits successful runs and aborts runs with errors. It also respects 'dry-run' command-line option. """ death_row = getDeathRow(archive, self.logger, self.options.pool_root) self.logger.debug("Unpublishing death row for %s." % archive.displayname) set_request_started(request_statements=LimitedList(10000), txn=self.txn, enable_timeout=False) try: death_row.reap(self.options.dry_run) except Exception: self.logger.exception( "Unexpected exception while doing death-row unpublish") self.txn.abort() else: if self.options.dry_run: self.logger.info("Dry run mode; rolling back.") self.txn.abort() else: self.logger.debug("Committing") self.txn.commit() finally: clear_request_started()
def test_store_disconnected_after_request_handled_logs_oops(self): # Bug #504291 was that a Store was being left in a disconnected # state after a request, causing subsequent requests handled by that # thread to fail. We detect this state in endRequest and log an # OOPS to help track down the trigger. request = LaunchpadTestRequest() publication = WebServicePublication(None) dbadapter.set_request_started() # Disconnect a store store = IMasterStore(EmailAddress) store._connection._state = STATE_DISCONNECTED # Invoke the endRequest hook. publication.endRequest(request, None) self.assertEqual(1, len(self.oopses)) oops = self.oopses[0] # Ensure the OOPS mentions the correct exception self.assertStartsWith(oops['value'], "Bug #504291") # Ensure the store has been rolled back and in a usable state. self.assertEqual(store._connection._state, STATE_RECONNECT) store.find(EmailAddress).first() # Confirms Store is working.
def processForDistro(self, distribution): """Process all queue items for a distribution. Commits between items. :param distribution: The `Distribution` to process queue items for. :return: A list of all successfully processed items' ids. """ processed_queue_ids = [] for archive in self.getTargetArchives(distribution): set_request_started(request_statements=LimitedList(10000), txn=self.txn, enable_timeout=False) try: for distroseries in distribution.series: self.logger.debug("Processing queue for %s %s" % (archive.reference, distroseries.name)) queue_items = distroseries.getPackageUploads( status=PackageUploadStatus.ACCEPTED, archive=archive) for queue_item in queue_items: if self.processQueueItem(queue_item): processed_queue_ids.append(queue_item.id) # Commit even on error; we may have altered the # on-disk archive, so the partial state must # make it to the DB. self.txn.commit() close_bugs_for_queue_item(queue_item) self.txn.commit() finally: clear_request_started() return processed_queue_ids
def test_reduced_timeout(self): """reduced_timeout caps the available timeout in various ways.""" self.addCleanup(set_default_timeout_function, None) with reduced_timeout(1.0): self.assertIsNone(get_default_timeout_function()()) with reduced_timeout(1.0, default=5.0): self.assertEqual(5.0, get_default_timeout_function()()) set_default_timeout_function(lambda: 5.0) with reduced_timeout(1.0): self.assertEqual(4.0, get_default_timeout_function()()) with reduced_timeout(1.0, default=5.0, webapp_max=2.0): self.assertEqual(4.0, get_default_timeout_function()()) with reduced_timeout(1.0, default=5.0, webapp_max=6.0): self.assertEqual(4.0, get_default_timeout_function()()) with reduced_timeout(6.0, default=5.0, webapp_max=2.0): self.assertEqual(5.0, get_default_timeout_function()()) LaunchpadTestRequest() set_request_started() try: with reduced_timeout(1.0): self.assertEqual(4.0, get_default_timeout_function()()) with reduced_timeout(1.0, default=5.0, webapp_max=2.0): self.assertEqual(2.0, get_default_timeout_function()()) with reduced_timeout(1.0, default=5.0, webapp_max=6.0): self.assertEqual(4.0, get_default_timeout_function()()) with reduced_timeout(6.0, default=5.0, webapp_max=2.0): self.assertEqual(2.0, get_default_timeout_function()()) finally: clear_request_started()
def rewriteLine(self, resource_location): """Rewrite 'resource_location' to a more concrete location. We use the 'translatePath' BranchFileSystemClient method. There are three cases: (1) The request is for something within the .bzr directory of a branch. In this case we rewrite the request to the location from which branches are served by ID. (2) The request is for something within a branch, but not the .bzr directory. In this case, we hand the request off to codebrowse. (3) The branch is not found. Two sub-cases: the request is for a product control directory or the we don't know how to translate the path. In both these cases we return 'NULL' which indicates to Apache that we don't know how to rewrite the request (and so it should go on to generate a 404 response). Other errors are allowed to propagate, on the assumption that the caller will catch and log them. """ # Codebrowse generates references to its images and stylesheets # starting with "/static", so pass them on unthinkingly. T = time.time() # Tell the webapp adapter that we are in a request, so that DB # statement timeouts will be applied. set_request_started() try: cached = None if resource_location.startswith('/static/'): r = self._codebrowse_url(resource_location) cached = 'N/A' else: branch_id, trailing, cached = self._getBranchIdAndTrailingPath( resource_location) if branch_id is None: if resource_location.startswith('/' + BRANCH_ID_ALIAS_PREFIX): r = 'NULL' else: r = self._codebrowse_url(resource_location) else: if trailing.startswith('/.bzr'): r = urlutils.join( config.codehosting.internal_branch_by_id_root, branch_id_to_path(branch_id), trailing[1:]) else: r = self._codebrowse_url(resource_location) finally: clear_request_started() self.logger.info("%r -> %r (%fs, cache: %s)", resource_location, r, time.time() - T, cached) return r
def runJobHandleError(self, job, fallback=None): set_request_started(enable_timeout=False, detail_filter=job.timeline_detail_filter) try: return super(BaseJobRunner, self).runJobHandleError(job, fallback=fallback) finally: clear_request_started()
def test_disconnect_logs_oops(self): # Ensure that OOPS reports are generated for database # disconnections, as per Bug #373837. request = LaunchpadTestRequest() publication = WebServicePublication(None) dbadapter.set_request_started() try: raise DisconnectionError('Fake') except DisconnectionError: self.assertRaises(Retry, publication.handleException, None, request, sys.exc_info(), True) dbadapter.clear_request_started() self.assertEqual(1, len(self.oopses)) oops = self.oopses[0] # Ensure the OOPS mentions the correct exception self.assertEqual(oops['type'], "DisconnectionError")
def endRequest(self, path='/', exception=None, pageid=None, work=None): start_event = self._get_start_event(path) da.set_request_started() profile.start_request(start_event) request = start_event.request if pageid is not None: request.setInWSGIEnvironment('launchpad.pageid', pageid) if work is not None: work() request.response.setResult(EXAMPLE_HTML) context = object() event = EndRequestEvent(context, request) if exception is not None: self.eru.raising((type(exception), exception, None), event.request) profile.end_request(event) da.clear_request_started() return event.request
def endRequest(self, path='/', exception=None, pageid=None, work=None): start_event = self._get_start_event(path) da.set_request_started() profile.start_request(start_event) request = start_event.request if pageid is not None: request.setInWSGIEnvironment('launchpad.pageid', pageid) if work is not None: work() request.response.setResult(EXAMPLE_HTML) context = object() event = EndRequestEvent(context, request) if exception is not None: self.eru.raising( (type(exception), exception, None), event.request) profile.end_request(event) da.clear_request_started() return event.request
def test_disconnect_logs_oops(self): # Ensure that OOPS reports are generated for database # disconnections, as per Bug #373837. request = LaunchpadTestRequest() publication = WebServicePublication(None) dbadapter.set_request_started() try: raise DisconnectionError('Fake') except DisconnectionError: self.assertRaises( Retry, publication.handleException, None, request, sys.exc_info(), True) dbadapter.clear_request_started() self.assertEqual(1, len(self.oopses)) oops = self.oopses[0] # Ensure the OOPS mentions the correct exception self.assertEqual(oops['type'], "DisconnectionError")
def beginErrorHandlingTransaction(self, request, ob, note): """Hook for when a new view is started to handle an exception. We need to add an additional behavior to the usual Zope behavior. We must restart the request timer. Otherwise we can get OOPS errors from our exception views inappropriately. """ super(LaunchpadBrowserPublication, self).beginErrorHandlingTransaction(request, ob, note) # XXX: gary 2008-11-04 bug=293614: As the bug describes, we want to # only clear the SQL records and timeout when we are preparing for a # view (or a side effect). Otherwise, we don't want to clear the # records because they are what the error reporting utility uses to # create OOPS reports with the SQL commands that led up to the error. # At the moment, we can only distinguish based on the "note" argument: # an undocumented argument of this undocumented method. if note in ('application error-handling', 'application error-handling side-effect'): da.clear_request_started() da.set_request_started()
def beforeTraversal(self, request): notify(StartRequestEvent(request)) request._traversalticks_start = tickcount.tickcount() threadid = thread.get_ident() threadrequestfile = open_for_writing( 'logs/thread-%s.request' % threadid, 'w') try: request_txt = unicode(request).encode('UTF-8') except Exception: request_txt = 'Exception converting request to string\n\n' try: request_txt += traceback.format_exc() except: request_txt += 'Unable to render traceback!' threadrequestfile.write(request_txt) threadrequestfile.close() # Tell our custom database adapter that the request has started. da.set_request_started() newInteraction(request) transaction.begin() # Now we are logged in, install the correct IDatabasePolicy for # this request. db_policy = IDatabasePolicy(request) getUtility(IStoreSelector).push(db_policy) getUtility(IOpenLaunchBag).clear() # Set the default layer. adapters = getGlobalSiteManager().adapters layer = adapters.lookup((providedBy(request), ), IDefaultSkin, '') if layer is not None: layers.setAdditionalLayer(request, layer) principal = self.getPrincipal(request) request.setPrincipal(principal) self.maybeRestrictToTeam(request) maybe_block_offsite_form_post(request)
def beforeTraversal(self, request): notify(StartRequestEvent(request)) request._traversalticks_start = tickcount.tickcount() threadid = thread.get_ident() threadrequestfile = open_for_writing( 'logs/thread-%s.request' % threadid, 'w') try: request_txt = unicode(request).encode('UTF-8') except Exception: request_txt = 'Exception converting request to string\n\n' try: request_txt += traceback.format_exc() except: request_txt += 'Unable to render traceback!' threadrequestfile.write(request_txt) threadrequestfile.close() # Tell our custom database adapter that the request has started. da.set_request_started() newInteraction(request) transaction.begin() # Now we are logged in, install the correct IDatabasePolicy for # this request. db_policy = IDatabasePolicy(request) getUtility(IStoreSelector).push(db_policy) getUtility(IOpenLaunchBag).clear() # Set the default layer. adapters = getGlobalSiteManager().adapters layer = adapters.lookup((providedBy(request),), IDefaultSkin, '') if layer is not None: layers.setAdditionalLayer(request, layer) principal = self.getPrincipal(request) request.setPrincipal(principal) self.maybeRestrictToTeam(request) maybe_block_offsite_form_post(request)
def test_timeout(self): # The time permitted to get the diff from the librarian may be None, # or 2. If there is not 2 seconds left in the request, the number will # be 0.01 smaller or the actual remaining time. class DiffWithFakeText(Diff): diff_text = FakeMethod() diff = DiffWithFakeText() diff.diff_text.open = FakeMethod() diff.diff_text.read = FakeMethod() diff.diff_text.close = FakeMethod() value = None original_timeout_function = get_default_timeout_function() set_default_timeout_function(lambda: value) try: LaunchpadTestRequest() set_request_started() try: diff.text self.assertEqual( LIBRARIAN_SERVER_DEFAULT_TIMEOUT, diff.diff_text.open.calls[-1][0][0]) value = 3.1 diff.text self.assertEqual(2.0, diff.diff_text.open.calls[-1][0][0]) value = 1.11 diff.text self.assertEqual(1.1, diff.diff_text.open.calls[-1][0][0]) value = 0.11 diff.text self.assertEqual(0.1, diff.diff_text.open.calls[-1][0][0]) value = 0.01 diff.text self.assertEqual(0.01, diff.diff_text.open.calls[-1][0][0]) finally: clear_request_started() finally: set_default_timeout_function(original_timeout_function)
def _statement_logging_start(self): """Start logging SQL statements and other database activity.""" set_request_started( request_statements=LimitedList(MAX_SQL_STATEMENTS_LOGGED), txn=self._transaction_manager, enable_timeout=False)
def setUp(self): super(TestLoggingWithinRequest, self).setUp() self.connection = StubConnection() self.person = self.factory.makePerson() da.set_request_started(1000.0) self.addCleanup(da.clear_request_started)
def rewriteLine(self, resource_location): """Rewrite 'resource_location' to a more concrete location. We use the 'translatePath' BranchFileSystemClient method. There are three cases: (1) The request is for something within the .bzr directory of a branch. In this case we rewrite the request to the location from which branches are served by ID. (2) The request is for something within a branch, but not the .bzr directory. In this case, we hand the request off to codebrowse. (3) The branch is not found. Two sub-cases: the request is for a product control directory or the we don't know how to translate the path. In both these cases we return 'NULL' which indicates to Apache that we don't know how to rewrite the request (and so it should go on to generate a 404 response). Other errors are allowed to propagate, on the assumption that the caller will catch and log them. """ # Codebrowse generates references to its images and stylesheets # starting with "/static", so pass them on unthinkingly. T = time.time() # Tell the webapp adapter that we are in a request, so that DB # statement timeouts will be applied. set_request_started() try: cached = None if resource_location.startswith('/static/'): r = self._codebrowse_url(resource_location) cached = 'N/A' else: branch_id, trailing, cached = self._getBranchIdAndTrailingPath( resource_location) if branch_id is None: if resource_location.startswith( '/' + BRANCH_ID_ALIAS_PREFIX): r = 'NULL' else: r = self._codebrowse_url(resource_location) else: if trailing.startswith('/.bzr'): r = urlutils.join( config.codehosting.internal_branch_by_id_root, branch_id_to_path(branch_id), trailing[1:]) else: r = self._codebrowse_url(resource_location) finally: clear_request_started() self.logger.info( "%r -> %r (%fs, cache: %s)", resource_location, r, time.time() - T, cached) return r