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 Test, so if a test suite gets 22 deprecated or renamed, there may be a set of related StoppageAlerts created. 23 24 The key for a StoppageAlert is of the form: 25 [("StoppageAlertParent", <test_path>), ("StoppageAlert", <revision>)]. 26 27 Thus, two StoppageAlert entities can not be created for the same stoppage 28 event. 29 """ 30 # Whether a mail notification has been sent for this alert. 31 mail_sent = ndb.BooleanProperty(indexed=True, default=False) 32 33 # Whether new points have been received for the test after this alert. 34 recovered = ndb.BooleanProperty(indexed=True, default=False) 35 36 # Computed properties are treated like member variables, so they have 37 # lowercase names, even though they look like methods to pylint. 38 # pylint: disable=invalid-name 39 40 @ndb.ComputedProperty 41 def revision(self): 42 return self.key.id() 43 44 @ndb.ComputedProperty 45 def test(self): 46 return utils.TestKey(self.key.parent().string_id()) 47 48 @ndb.ComputedProperty 49 def row(self): 50 test_container = utils.GetTestContainerKey(self.test) 51 return ndb.Key('Row', self.revision, parent=test_container) 52 53 @ndb.ComputedProperty 54 def start_revision(self): 55 return self.revision 56 57 @ndb.ComputedProperty 58 def end_revision(self): 59 return self.revision 60 61 @ndb.ComputedProperty 62 def last_row_date(self): 63 row = self.row.get() 64 if not row: 65 logging.warning('No Row with key %s', self.row) 66 return None 67 return row.timestamp 68 69 70def GetStoppageAlert(test_path, revision): 71 """Gets a StoppageAlert entity if it already exists. 72 73 Args: 74 test_path: The test path string of the Test associated with the alert. 75 revision: The ID of the last point before the stoppage. 76 77 Returns: 78 A StoppageAlert entity or None. 79 """ 80 return ndb.Key( 81 'StoppageAlertParent', test_path, 'StoppageAlert', revision).get() 82 83 84def CreateStoppageAlert(test, row): 85 """Creates a new StoppageAlert entity. 86 87 Args: 88 test: A Test entity. 89 row: A Row entity; the last Row that was put before the stoppage. 90 91 Returns: 92 A new StoppageAlert entity which has not been put, or None, 93 if we don't want to create a new StoppageAlert. 94 """ 95 new_alert = StoppageAlert( 96 parent=ndb.Key('StoppageAlertParent', test.test_path), 97 id=row.revision, 98 internal_only=test.internal_only, 99 sheriff=test.sheriff) 100 alert_group.GroupAlerts([new_alert], test.suite_name, 'StoppageAlert') 101 grouped_alert_keys = StoppageAlert.query( 102 StoppageAlert.group == new_alert.group).fetch(keys_only=True) 103 if len(grouped_alert_keys) >= _MAX_GROUP_SIZE: 104 # Too many stoppage alerts in this group; we don't want to put any more. 105 return None 106 test.stoppage_alert = new_alert.key 107 test.put() 108 return new_alert 109