Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Side by Side Diff: infra/libs/buildbot/master.py

Issue 1097233002: Add libs to get information about or manipulate a buildbot master. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@timebrug
Patch Set: Clarify comment. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « infra/libs/buildbot/__init__.py ('k') | infra/libs/buildbot/test/__init__.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 """Get information about a buildbot master and manipulate it."""
6
7
8 import errno
9 import os
10 import json
11 import re
12 import requests
13 import simplejson
14 import subprocess
15 import sys
16
17 from dateutil.parser import parse
18
19 from infra.libs.time_functions import timestamp
20
21 ######## Getting information about the master.
22
23
24 # Derived from http://stackoverflow.com/a/6940314.
25 def _pid_is_alive(pid): # pragma: no cover
26 """Determine if the given pid is still running.
27
28 Sending a signal of 0 (the 'null signal') does all the checks to send a signal
29 without actually sending a signal. Thus it works as a check to see if the pid
30 is valid. See http://goo.gl/6M1BoF for details.
31 """
32 if sys.platform.startswith('win'):
33 raise NotImplementedError
34
35 if pid <= 0:
36 return False
37 try:
38 os.kill(pid, 0)
39 except OSError as err:
40 if err.errno == errno.ESRCH:
41 return False
42 elif err.errno == errno.EPERM:
43 return True
44 else:
45 raise
46 else:
47 return True
48
49
50 def buildbot_is_running(directory):
51 """Determine if the twisted.pid in a directory is running."""
52 pidfile = os.path.join(directory, 'twistd.pid')
53 if not os.path.exists(pidfile):
54 return False
55 with open(pidfile) as f:
56 pid = int(f.read().strip())
57 return _pid_is_alive(pid)
58
59
60 def _get_last_action(directory, action):
61 """Get the last time the given action was run in the actions.log."""
62 actions_log_file = os.path.join(directory, 'actions.log')
63 if not os.path.exists(actions_log_file):
64 return None
65 with open(actions_log_file) as f:
66 for line in reversed(list(f)):
67 if action in line:
68 unparsed_date = line.split(action)[0][len('**'):]
69 return timestamp.utctimestamp(parse(unparsed_date))
70 return None
71
72
73
74 def get_last_boot(directory):
75 """Determine the last time the master was started."""
76 return max(
77 _get_last_action(directory, 'make restart'),
78 _get_last_action(directory, 'make start'))
79
80
81 def get_last_no_new_builds(directory):
82 """Determine the last time a *current* make no-new-builds was called.
83
84 If a 'make start' or 'make restart' was issued after the no-new-builds, it is
85 not valid (since the make start or make restart nullified the no-new-builds).
86 """
87 last_boot = get_last_boot(directory)
88 last_no_new_builds = _get_last_action(directory, 'make no-new-builds')
89 if not last_no_new_builds:
90 return None
91 if last_boot > last_no_new_builds:
92 return None
93 return last_no_new_builds
94
95
96 def _get_mastermap_data(directory):
97 """Get mastermap JSON from a master directory."""
98
99 build_dir = os.path.join(directory, os.pardir, os.pardir, os.pardir, 'build')
100 runit = os.path.join(build_dir, 'scripts', 'tools', 'runit.py')
101 build_internal_dir = os.path.join(build_dir, os.pardir, 'build_internal')
102
103 script_path = os.path.join(build_dir, 'scripts', 'tools', 'mastermap.py')
104 if os.path.exists(build_internal_dir):
105 script_path = os.path.join(
106 build_internal_dir, 'scripts', 'tools', 'mastermap_internal.py')
107
108 script_data = json.loads(subprocess.check_output(
109 [runit, script_path, '-f', 'json']))
110
111 short_dirname = os.path.basename(directory)
112 matches = [x for x in script_data if x.get('dirname') == short_dirname]
113 assert len(matches) < 2, (
114 'Multiple masters were found with the same master directory.')
115 if matches:
116 return matches[0]
117 return None
118
119
120 def _get_master_web_port(directory):
121 """Determine the web port of the master running in the given directory."""
122 mastermap_data = _get_mastermap_data(directory)
123 if mastermap_data:
124 return mastermap_data['port']
125 return None
126
127
128 def get_accepting_builds(directory, timeout=30):
129 """Determine whether the master is accepting new builds or not.
130
131 *** This only works for masters running on localhost.***
132 """
133 port = _get_master_web_port(directory)
134 if port is not None:
135 try:
136 res = requests.get(
137 'http://localhost:%d/json/accepting_builds' % port,
138 timeout=timeout)
139 if res.status_code == 200:
140 try:
141 return res.json().get('accepting_builds')
142 except simplejson.scanner.JSONDecodeError:
143 pass
144 except requests.exceptions.Timeout:
145 pass
146 return None
147
148
149 ######## Performing actions on the master.
150
151
152 GclientSync, MakeStop, MakeWait, MakeStart, MakeNoNewBuilds = range(5)
153
154
155 def convert_action_items_to_cli(
156 action_items, directory, enable_gclient=False):
157
158 def cmd_dict(cmd, lockfile_prefix):
159 # Using the same lockfile prefix for two actions will make them mutually
160 # exclusive. So setting 'make' for all the make commands tries to prevent
161 # two make actions happening at once in the same master directory.
162 lockfile = '%s_%s' % (
163 lockfile_prefix,
164 re.sub('[^\w]', '_', directory.lower()))
165 return {
166 'cwd': directory,
167 'cmd': cmd,
168 'lockfile': lockfile,
169 }
170
171 for action_item in action_items:
172 if action_item == GclientSync:
173 if enable_gclient:
174 yield cmd_dict(
175 ['gclient', 'sync', '--reset', '--force', '--auto_rebase'],
176 'gclient')
177 elif action_item == MakeStop:
178 yield cmd_dict(['make', 'stop'], 'make')
179 elif action_item == MakeWait:
180 yield cmd_dict(['make', 'wait'], 'make')
181 elif action_item == MakeStart:
182 yield cmd_dict(['make', 'start'], 'make')
183 elif action_item == MakeNoNewBuilds:
184 yield cmd_dict(['make', 'no-new-builds'], 'make')
185 else:
186 raise ValueError('Invalid action item %s' % action_item)
OLDNEW
« no previous file with comments | « infra/libs/buildbot/__init__.py ('k') | infra/libs/buildbot/test/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698