Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 6 """A state machine to determine and act on a buildbot master's state.""" | |
| 7 | |
| 8 | |
| 9 from infra.libs.state_machine import pattern_match | |
| 10 from infra.libs.time_functions import timestamp | |
| 11 from infra.libs.buildbot import master | |
| 12 | |
| 13 | |
| 14 STATES = { | |
|
agable
2015/04/30 21:23:30
Love it, this set of states is great.
| |
| 15 'buildbot': [ | |
| 16 'offline', | |
| 17 'starting', | |
| 18 'running', | |
| 19 'draining', | |
| 20 'drained', | |
| 21 'crashed', | |
| 22 ], | |
| 23 'desired_buildbot_state': [ | |
| 24 'offline', | |
| 25 'running', | |
| 26 'drained', | |
| 27 ], | |
| 28 'desired_transition_time': [ | |
| 29 'ready_to_fire', | |
| 30 'hold_steady', | |
| 31 ], | |
| 32 } | |
| 33 | |
| 34 | |
| 35 def collect_evidence(master_directory, connection_timeout=30): | |
| 36 """Collects evidence from the OS for late state determination.""" | |
| 37 evidence = {} | |
| 38 evidence['now'] = timestamp.utcnow_ts() | |
| 39 evidence['last_boot'] = master.get_last_boot(master_directory) | |
| 40 evidence['last_no_new_builds'] = master.get_last_no_new_builds( | |
| 41 master_directory) | |
| 42 evidence['buildbot_is_running'] = master.buildbot_is_running(master_directory) | |
| 43 | |
| 44 if evidence['buildbot_is_running']: | |
| 45 evidence['accepting_builds'] = master.get_accepting_builds( | |
| 46 master_directory, timeout=connection_timeout) | |
| 47 | |
| 48 return evidence | |
| 49 | |
| 50 | |
| 51 def construct_pattern_matcher( | |
| 52 boot_timeout_sec=5 * 60, drain_timeout_sec=5 * 60): | |
| 53 # There is a bug in pylint which triggers false positives on decorated | |
| 54 # decorators with arguments: http://goo.gl/Ln6uyn | |
| 55 # pylint: disable=no-value-for-parameter | |
| 56 matchlist = pattern_match.MatchList(STATES) | |
| 57 | |
| 58 @matchlist.add_match( | |
| 59 buildbot='running', | |
| 60 desired_buildbot_state='running', | |
| 61 desired_transition_time='hold_steady') | |
| 62 @matchlist.add_match( | |
| 63 buildbot='drained', | |
| 64 desired_buildbot_state='drained', | |
| 65 desired_transition_time='hold_steady') | |
| 66 @matchlist.add_match( | |
| 67 buildbot='offline', | |
| 68 desired_buildbot_state='offline') | |
| 69 @matchlist.add_match( | |
| 70 buildbot='starting', | |
| 71 exclusions={'desired_buildbot_state': ['offline']}) | |
| 72 @matchlist.add_match( | |
| 73 buildbot='draining') | |
| 74 def _do_nothing(): | |
| 75 return [] | |
| 76 | |
| 77 @matchlist.add_match( | |
| 78 buildbot='drained', | |
| 79 desired_buildbot_state='running') | |
| 80 @matchlist.add_match( | |
| 81 buildbot='drained', | |
| 82 desired_buildbot_state='drained', | |
| 83 desired_transition_time='ready_to_fire') | |
| 84 @matchlist.add_match( | |
| 85 buildbot='crashed', | |
| 86 exclusions={'desired_buildbot_state': ['offline']}) | |
| 87 def _make_restart(): | |
| 88 return [ | |
| 89 master.GclientSync, master.MakeStop, master.MakeWait, master.MakeStart] | |
| 90 | |
| 91 @matchlist.add_match( | |
| 92 buildbot='running', | |
| 93 desired_buildbot_state='running', | |
| 94 desired_transition_time='ready_to_fire') | |
| 95 @matchlist.add_match( | |
| 96 buildbot='running', | |
| 97 desired_buildbot_state='offline') | |
| 98 @matchlist.add_match( | |
| 99 buildbot='running', | |
| 100 desired_buildbot_state='drained') | |
| 101 def _make_no_new_builds(): | |
| 102 return [master.MakeNoNewBuilds] | |
| 103 | |
| 104 @matchlist.add_match( | |
| 105 buildbot='offline', | |
| 106 exclusions={ | |
| 107 'desired_buildbot_state': ['offline'], | |
| 108 }) | |
| 109 def _make_start(): | |
| 110 return [master.GclientSync, master.MakeStart] | |
| 111 | |
| 112 @matchlist.add_match( | |
| 113 buildbot='crashed', | |
| 114 desired_buildbot_state='offline') | |
| 115 @matchlist.add_match( | |
| 116 buildbot='starting', | |
| 117 desired_buildbot_state='offline') | |
| 118 @matchlist.add_match( | |
| 119 buildbot='drained', | |
| 120 desired_buildbot_state='offline') | |
| 121 def _make_stop(): | |
| 122 return [master.MakeStop] | |
| 123 | |
| 124 @matchlist.add_detector('buildbot') | |
| 125 def _check_buildbot_state(data): | |
| 126 if not data['buildbot_is_running']: | |
| 127 return 'offline' | |
| 128 if data['accepting_builds'] is None: | |
| 129 if data['last_boot'] > (data['now'] - boot_timeout_sec): | |
| 130 return 'starting' | |
| 131 return 'crashed' | |
| 132 if data['accepting_builds']: | |
| 133 return 'running' | |
| 134 if data['last_no_new_builds'] > (data['now'] - drain_timeout_sec): | |
| 135 return 'draining' | |
| 136 return 'drained' | |
| 137 | |
| 138 @matchlist.add_detector('desired_buildbot_state') | |
| 139 def _check_desired_state(data): | |
| 140 desired_state = data['desired_buildbot_state']['desired_state'] | |
| 141 if desired_state in STATES['desired_buildbot_state']: | |
| 142 return desired_state | |
| 143 | |
| 144 raise ValueError('%s is not a valid desired_buildbot_state' % desired_state) | |
| 145 | |
| 146 @matchlist.add_detector('desired_transition_time') | |
| 147 def _check_transition_time(data): | |
| 148 transition_time = data['desired_buildbot_state']['transition_time_utc'] | |
| 149 if transition_time > data['now']: | |
| 150 # If we specify a date in the future and request 'running', the state | |
| 151 # machine will continually reboot buildbot until that time is reached. | |
| 152 raise ValueError( | |
| 153 'specifying a date in the future creates ambiguity about now') | |
| 154 if transition_time >= data['last_boot']: | |
| 155 return 'ready_to_fire' | |
| 156 return 'hold_steady' | |
| 157 | |
| 158 | |
| 159 assert matchlist.is_correct | |
| 160 return matchlist | |
| OLD | NEW |