1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""The datastore model for alerts when data is no longer received for a test.""" 6 7import logging 8 9from google.appengine.ext import ndb 10 11from dashboard import utils 12from dashboard.models import alert 13from dashboard.models import alert_group 14 15_MAX_GROUP_SIZE = 20 16 17 18class StoppageAlert(alert.Alert): 19 """A stoppage alert is an alert for a test no longer receiving new points. 20 21 Each StoppageAlert is associated with one TestMetadata, so if a test suite 22 gets deprecated or renamed, there may be a set of related StoppageAlerts 23 created. 24 25 The key for a StoppageAlert is of the form: 26 [("StoppageAlertParent", <test_path>), ("StoppageAlert", <revision>)]. 27 28 Thus, two StoppageAlert entities can not be created for the same stoppage 29 event. 30 """ 31 # Whether a mail notification has been sent for this alert. 32 mail_sent = ndb.BooleanProperty(indexed=True, default=False) 33 34 # Whether new points have been received for the test after this alert. 35 recovered = ndb.BooleanProperty(indexed=True, default=False) 36 37 # Computed properties are treated like member variables, so they have 38 # lowercase names, even though they look like methods to pylint. 39 # pylint: disable=invalid-name 40 41 @ndb.ComputedProperty 42 def revision(self): 43 return self.key.id() 44 45 @ndb.ComputedProperty 46 def test(self): 47 return utils.TestKey(self.key.parent().string_id()) 48 49 @ndb.ComputedProperty 50 def row(self): 51 test_container = utils.GetTestContainerKey(self.GetTestMetadataKey()) 52 return ndb.Key('Row', self.revision, parent=test_container) 53 54 @ndb.ComputedProperty 55 def start_revision(self): 56 return self.revision 57 58 @ndb.ComputedProperty 59 def end_revision(self): 60 return self.revision 61 62 @ndb.ComputedProperty 63 def last_row_date(self): 64 row = self.row.get() 65 if not row: 66 logging.warning('No Row with key %s', self.row) 67 return None 68 return row.timestamp 69 70 71def GetStoppageAlert(test_path, revision): 72 """Gets a StoppageAlert entity if it already exists. 73 74 Args: 75 test_path: The test path string of the TestMetadata associated with the 76 alert. 77 revision: The ID of the last point before the stoppage. 78 79 Returns: 80 A StoppageAlert entity or None. 81 """ 82 return ndb.Key( 83 'StoppageAlertParent', test_path, 'StoppageAlert', revision).get() 84 85 86def CreateStoppageAlert(test, row): 87 """Creates a new StoppageAlert entity. 88 89 Args: 90 test: A TestMetadata entity. 91 row: A Row entity; the last Row that was put before the stoppage. 92 93 Returns: 94 A new StoppageAlert entity which has not been put, or None, 95 if we don't want to create a new StoppageAlert. 96 """ 97 new_alert = StoppageAlert( 98 parent=ndb.Key('StoppageAlertParent', test.test_path), 99 id=row.revision, 100 internal_only=test.internal_only, 101 sheriff=test.sheriff) 102 alert_group.GroupAlerts([new_alert], test.suite_name, 'StoppageAlert') 103 grouped_alert_keys = StoppageAlert.query( 104 StoppageAlert.group == new_alert.group).fetch(keys_only=True) 105 if len(grouped_alert_keys) >= _MAX_GROUP_SIZE: 106 # Too many stoppage alerts in this group; we don't want to put any more. 107 return None 108 test.stoppage_alert = new_alert.key 109 test.put() 110 return new_alert 111