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 | |
|
agable
2015/04/27 20:01:45
Since the only file in this directory is called ma
ghost stip (do not use)
2015/04/27 23:42:18
Done.
| |
| 5 """Get information about a buildbot master and manipulate it.""" | |
| 6 | |
| 7 | |
| 8 import errno | |
| 9 import os | |
| 10 import json | |
| 11 from dateutil.parser import parse | |
|
agable
2015/04/27 20:01:45
nit: "from" import statements come in a separate b
ghost stip (do not use)
2015/04/27 23:42:18
Done.
| |
| 12 import re | |
| 13 import requests | |
| 14 import simplejson | |
| 15 import subprocess | |
| 16 import sys | |
| 17 | |
| 18 from infra.libs.time_functions import timestamp | |
| 19 | |
| 20 ######## Getting information about the master. | |
| 21 | |
| 22 | |
| 23 # Derived from http://stackoverflow.com/a/6940314. | |
|
agable
2015/04/27 20:01:45
Include in the comment here or in the docstring th
ghost stip (do not use)
2015/04/27 23:42:17
Done.
| |
| 24 def _pid_is_alive(pid): # pragma: no cover | |
| 25 """Determine if the given pid is still running.""" | |
| 26 if sys.platform.startswith('win'): | |
| 27 raise NotImplementedError | |
| 28 if pid <= 0: | |
| 29 return False | |
| 30 try: | |
| 31 os.kill(pid, 0) | |
| 32 except OSError as err: | |
| 33 if err.errno == errno.ESRCH: | |
| 34 return False | |
| 35 elif err.errno == errno.EPERM: | |
| 36 return True | |
| 37 else: | |
| 38 raise | |
| 39 else: | |
| 40 return True | |
| 41 | |
| 42 | |
| 43 def buildbot_is_running(directory): | |
| 44 """Determine if the twisted.pid in a directory is running.""" | |
| 45 pidfile = os.path.join(directory, 'twistd.pid') | |
| 46 if not os.path.exists(pidfile): | |
| 47 return False | |
| 48 with open(pidfile) as f: | |
| 49 pid = int(f.read().strip()) | |
| 50 return _pid_is_alive(pid) | |
| 51 | |
| 52 | |
| 53 def _get_last_action(directory, action): | |
| 54 """Get the last time the given action was run in the actions.log.""" | |
| 55 actions_log_file = os.path.join(directory, 'actions.log') | |
| 56 if not os.path.exists(actions_log_file): | |
| 57 return None | |
| 58 last_line = None | |
| 59 with open(actions_log_file) as f: | |
| 60 for line in f: | |
|
agable
2015/04/27 20:01:45
Read the file backwards and search for the first l
ghost stip (do not use)
2015/04/27 23:42:18
it's way cleaner now. nice
| |
| 61 if action in line: | |
| 62 last_line = line | |
| 63 if not last_line: | |
| 64 return None | |
| 65 | |
| 66 unparsed_date = last_line.split(action)[0][len('**'):] | |
| 67 return timestamp.utctimestamp(parse(unparsed_date)) | |
| 68 | |
| 69 | |
| 70 def get_last_boot(directory): | |
| 71 """Determine the last time the master was started.""" | |
| 72 return max( | |
| 73 _get_last_action(directory, 'make restart'), | |
| 74 _get_last_action(directory, 'make start')) | |
| 75 | |
| 76 | |
| 77 def get_last_no_new_builds(directory): | |
| 78 """Determine the last time a *current* make no-new-builds was called. | |
| 79 | |
| 80 If a 'make start' or 'make restart' was issued after the no-new-builds, it is | |
| 81 not valid (since the make start or make restart nullified the no-new-builds). | |
| 82 """ | |
| 83 last_boot = get_last_boot(directory) | |
| 84 last_no_new_builds = _get_last_action(directory, 'make no-new-builds') | |
| 85 if not last_no_new_builds: | |
| 86 return None | |
| 87 if last_boot > last_no_new_builds: | |
| 88 return None | |
| 89 return last_no_new_builds | |
| 90 | |
| 91 | |
| 92 def _get_mastermap_data(directory): | |
| 93 """Get mastermap JSON from a master directory.""" | |
| 94 | |
| 95 build_dir = os.path.join(directory, os.pardir, os.pardir, os.pardir, 'build') | |
| 96 runit = os.path.join(build_dir, 'scripts', 'tools', 'runit.py') | |
| 97 build_internal_dir = os.path.join(build_dir, os.pardir, 'build_internal') | |
| 98 | |
| 99 script_path = os.path.join(build_dir, 'scripts', 'tools', 'mastermap.py') | |
| 100 if os.path.exists(build_internal_dir): | |
|
agable
2015/04/27 20:01:45
I'm a little leery about infra.git knowing about b
ghost stip (do not use)
2015/04/27 23:42:18
yeah. I actually with mastermap.py would just auto
| |
| 101 script_path = os.path.join( | |
| 102 build_internal_dir, 'scripts', 'tools', 'mastermap_internal.py') | |
| 103 | |
| 104 script_data = json.loads(subprocess.check_output( | |
| 105 [runit, script_path, '-f', 'json'])) | |
| 106 | |
| 107 short_dirname = os.path.basename(directory) | |
| 108 matches = [x for x in script_data if x.get('dirname') == short_dirname] | |
| 109 assert len(matches) < 2 | |
|
agable
2015/04/27 20:01:45
Add an error message, plain asserts in libraries m
ghost stip (do not use)
2015/04/27 23:42:18
Done.
| |
| 110 if matches: | |
| 111 return matches[0] | |
| 112 return None | |
| 113 | |
| 114 | |
| 115 def _get_master_web_port(directory): | |
| 116 """Determine the web port of the master running in the given directory.""" | |
| 117 mastermap_data = _get_mastermap_data(directory) | |
| 118 if mastermap_data: | |
| 119 return mastermap_data['port'] | |
| 120 return None | |
| 121 | |
| 122 | |
| 123 def get_accepting_builds(directory, timeout=30): | |
| 124 """Determine whether the master is accepting new builds or not.""" | |
|
agable
2015/04/27 20:01:45
Note in big huge words that this only works for ma
ghost stip (do not use)
2015/04/27 23:42:18
Done.
| |
| 125 port = _get_master_web_port(directory) | |
| 126 if port is not None: | |
| 127 try: | |
| 128 res = requests.get( | |
| 129 'http://localhost:%d/json/accepting_builds' % port, | |
| 130 timeout=timeout) | |
| 131 if res.status_code == 200: | |
| 132 try: | |
| 133 return res.json().get('accepting_builds') | |
| 134 except simplejson.scanner.JSONDecodeError: | |
| 135 pass | |
| 136 except requests.exceptions.Timeout: | |
| 137 pass | |
| 138 return None | |
| 139 | |
| 140 | |
| 141 ######## Performing actions on the master. | |
| 142 | |
| 143 | |
| 144 GclientSync, MakeStop, MakeWait, MakeStart, MakeNoNewBuilds = range(5) | |
| 145 | |
| 146 | |
| 147 def convert_action_items_to_cli( | |
| 148 action_items, directory, enable_gclient=False): | |
| 149 | |
| 150 def cmd_dict(cmd, lockfile_prefix): | |
| 151 # Using the same lockfile prefix for two actions will make them mutually | |
| 152 # exclusive. So setting 'make' for all the make commands tries to prevent | |
| 153 # two make actions happening at once in the same master directory. | |
| 154 lockfile = '%s_%s' % ( | |
| 155 lockfile_prefix, | |
| 156 re.sub('[^\w]', '_', directory.lower())) | |
| 157 return { | |
| 158 'cwd': directory, | |
| 159 'cmd': cmd, | |
| 160 'lockfile': lockfile, | |
| 161 } | |
| 162 | |
| 163 for action_item in action_items: | |
| 164 if action_item == GclientSync: | |
| 165 if enable_gclient: | |
| 166 yield cmd_dict( | |
| 167 ['gclient', 'sync', '--reset', '--force', '--auto_rebase'], | |
| 168 'gclient') | |
| 169 elif action_item == MakeStop: | |
| 170 yield cmd_dict(['make', 'stop'], 'make') | |
| 171 elif action_item == MakeWait: | |
| 172 yield cmd_dict(['make', 'wait'], 'make') | |
| 173 elif action_item == MakeStart: | |
| 174 yield cmd_dict(['make', 'start'], 'make') | |
| 175 elif action_item == MakeNoNewBuilds: | |
| 176 yield cmd_dict(['make', 'no-new-builds'], 'make') | |
| 177 else: | |
| 178 raise ValueError('Invalid action item %s' % action_item) | |
| OLD | NEW |