Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # This script greps the JSON files for the buildbots on the LLVM official |
| 4 | # build master by name and prints an HTML page with the links to the bots |
| 5 | # and the status. |
| 6 | # |
| 7 | # Multiple masters can be used, as well as multiple groups of bots and |
| 8 | # multiple bots per group, all in a json file. See linaro.json in this |
| 9 | # repository to have an idea how the config file is. |
| 10 | |
| 11 | import sys |
| 12 | import os |
| 13 | import argparse |
| 14 | import json |
| 15 | import tempfile |
| 16 | import logging |
David Spickett | 55cc06c | 2023-07-26 15:29:20 +0100 | [diff] [blame] | 17 | import pickle |
David Spickett | aa155be | 2021-02-25 14:30:09 +0000 | [diff] [blame] | 18 | import shutil |
David Spickett | 8822bbd | 2023-06-12 14:02:41 +0100 | [diff] [blame] | 19 | import time |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 20 | from datetime import datetime, timedelta |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 21 | |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 22 | # The requests allows HTTP keep-alive which re-uses the same TCP connection |
| 23 | # to download multiple files. |
| 24 | import requests |
David Spickett | 55449c6 | 2021-12-13 12:57:33 +0000 | [diff] [blame] | 25 | from textwrap import dedent |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 26 | from make_table import Table |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 27 | |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 28 | def ignored(s): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 29 | return "ignore" in s and s["ignore"] |
| 30 | |
| 31 | |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 32 | def not_ignored(s): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 33 | return not ignored(s) |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 34 | |
| 35 | |
David Spickett | e88fe59 | 2021-03-22 12:25:13 +0000 | [diff] [blame] | 36 | # Returns the parsed json URL or raises an exception |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 37 | def wget(session, url): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 38 | got = session.get(url) |
| 39 | got.raise_for_status() |
| 40 | return got.json() |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 41 | |
| 42 | |
Oliver Stannard | 91688ff | 2021-01-07 10:27:27 +0000 | [diff] [blame] | 43 | # Map from buildbot status codes we want to treat as errors to the color they |
| 44 | # should be shown in. The codes are documented at |
| 45 | # https://docs.buildbot.net/latest/developer/results.html#build-result-codes, |
| 46 | # and these colors match the suggested ones there. |
| 47 | RESULT_COLORS = { |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 48 | 2: "red", # Error |
| 49 | 4: "purple", # Exception |
| 50 | 5: "purple", # Retry |
| 51 | 6: "pink", # Cancelled |
Oliver Stannard | 91688ff | 2021-01-07 10:27:27 +0000 | [diff] [blame] | 52 | } |
| 53 | |
David Spickett | e88fe59 | 2021-03-22 12:25:13 +0000 | [diff] [blame] | 54 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 55 | def get_bot_failing_steps(session, base_url, buildid): |
| 56 | try: |
| 57 | contents = wget(session, "{}/api/v2/builds/{}/steps".format(base_url, buildid)) |
| 58 | except requests.exceptions.RequestException: |
| 59 | return "" |
| 60 | |
| 61 | for step in contents["steps"]: |
| 62 | if step["results"] in RESULT_COLORS: |
| 63 | yield (step["name"], step["results"]) |
Oliver Stannard | 91688ff | 2021-01-07 10:27:27 +0000 | [diff] [blame] | 64 | |
| 65 | |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 66 | # Get the status of a individual bot BOT. Returns a dict with the |
| 67 | # information. |
| 68 | def get_bot_status(session, bot, base_url, builder_url, build_url): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 69 | try: |
| 70 | builds = wget( |
| 71 | session, "{}/api/v2/{}/{}/{}".format(base_url, builder_url, bot, build_url) |
| 72 | ) |
| 73 | except requests.exceptions.RequestException as e: |
| 74 | logging.debug(" Couldn't get builds for bot {}!".format(bot)) |
| 75 | return {"valid": False} |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 76 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 77 | reversed_builds = iter(sorted(builds["builds"], key=lambda b: -b["number"])) |
| 78 | next_build = None |
| 79 | for build in reversed_builds: |
| 80 | if not build["complete"]: |
| 81 | next_build = build |
| 82 | continue |
David Spickett | 30a986f | 2021-04-29 09:37:00 +0100 | [diff] [blame] | 83 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 84 | time_since = int(datetime.now().timestamp()) - int(build["complete_at"]) |
| 85 | duration = int(build["complete_at"]) - int(build["started_at"]) |
| 86 | agent_url = "{}/#/{}/{}".format(base_url, builder_url, build["builderid"]) |
David Spickett | 30a986f | 2021-04-29 09:37:00 +0100 | [diff] [blame] | 87 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 88 | status = { |
| 89 | "builder_url": agent_url, |
| 90 | "number": build["number"], |
| 91 | "build_url": "{}/builds/{}".format(agent_url, build["number"]), |
| 92 | "state": build["state_string"], |
| 93 | "time_since": timedelta(seconds=time_since), |
| 94 | "duration": timedelta(seconds=duration), |
| 95 | "fail": build["state_string"] != "build successful", |
David Spickett | f850243 | 2023-07-07 15:52:26 +0100 | [diff] [blame] | 96 | "next_in_progress": None |
| 97 | if next_build is None |
| 98 | else "{}/builds/{}".format(agent_url, next_build["number"]), |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 99 | } |
David Spickett | 7f18f4d | 2021-03-22 11:49:17 +0000 | [diff] [blame] | 100 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 101 | if status["fail"]: |
| 102 | buildid = build["buildid"] |
| 103 | status["steps"] = list(get_bot_failing_steps(session, base_url, buildid)) |
David Spickett | 86f2d47 | 2023-06-12 11:21:16 +0100 | [diff] [blame] | 104 | |
Antoine Moynault | 0cbe02e | 2023-06-21 08:13:57 +0000 | [diff] [blame] | 105 | # find the start of the failure streak |
| 106 | first_fail = build |
| 107 | for build in reversed_builds: |
| 108 | if build["state_string"] == "build successful": |
| 109 | status["first_fail_number"] = first_fail["number"] |
| 110 | status["first_fail_url"] = "{}/builds/{}".format( |
| 111 | agent_url, first_fail["number"] |
| 112 | ) |
| 113 | fail_since = int(datetime.now().timestamp()) - int( |
| 114 | first_fail["complete_at"] |
| 115 | ) |
| 116 | status["fail_since"] = timedelta(seconds=fail_since) |
| 117 | break |
| 118 | first_fail = build |
| 119 | else: |
| 120 | pass # fails since forever? |
| 121 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 122 | return status |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 123 | |
| 124 | |
David Spickett | 85355fa | 2021-03-22 15:41:41 +0000 | [diff] [blame] | 125 | # Get status for all bots named in the config |
| 126 | # Return a dictionary of (base_url, bot name) -> status info |
David Spickett | 30a986f | 2021-04-29 09:37:00 +0100 | [diff] [blame] | 127 | def get_buildbot_bots_status(config): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 128 | session = requests.Session() |
| 129 | bot_cache = {} |
David Spickett | f006c37 | 2021-03-22 12:54:12 +0000 | [diff] [blame] | 130 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 131 | for server in filter(not_ignored, config): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 132 | base_url = server["base_url"] |
| 133 | logging.debug("Parsing server {}...".format(server["name"])) |
| 134 | for builder in server["builders"]: |
| 135 | logging.debug(" Parsing builders {}...".format(builder["name"])) |
| 136 | for bot in builder["bots"]: |
| 137 | bot_key = (base_url, bot["name"]) |
| 138 | if bot_key in bot_cache: |
| 139 | continue |
David Spickett | 85355fa | 2021-03-22 15:41:41 +0000 | [diff] [blame] | 140 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 141 | logging.debug(" Parsing bot {}...".format(bot["name"])) |
| 142 | status = get_bot_status( |
| 143 | session, |
| 144 | bot["name"], |
| 145 | base_url, |
| 146 | server["builder_url"], |
| 147 | server["build_url"], |
| 148 | ) |
| 149 | if status is not None: |
| 150 | if status.get("valid", True): |
| 151 | logging.debug( |
| 152 | " Bot status: " + ("FAIL" if status["fail"] else "PASS") |
| 153 | ) |
| 154 | bot_cache[bot_key] = status |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 155 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 156 | return bot_cache |
David Spickett | 85355fa | 2021-03-22 15:41:41 +0000 | [diff] [blame] | 157 | |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 158 | |
David Spickett | 85355fa | 2021-03-22 15:41:41 +0000 | [diff] [blame] | 159 | def write_bot_status(config, output_file, bots_status): |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 160 | temp = tempfile.NamedTemporaryFile(mode="w+", delete=False) |
David Spickett | f006c37 | 2021-03-22 12:54:12 +0000 | [diff] [blame] | 161 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 162 | temp.write( |
| 163 | dedent( |
| 164 | """\ |
David Spickett | 64161b0 | 2022-11-01 09:51:30 +0000 | [diff] [blame] | 165 | <!DOCTYPE html> |
David Spickett | 55449c6 | 2021-12-13 12:57:33 +0000 | [diff] [blame] | 166 | <style> |
| 167 | /* Combine the border between cells to prevent 1px gaps |
| 168 | in the row background colour. */ |
| 169 | table, td, th { |
| 170 | border-collapse: collapse; |
| 171 | } |
| 172 | /* Colour every other row in a table body grey. */ |
| 173 | tbody tr:nth-child(even) td { |
| 174 | background-color: #ededed; |
| 175 | } |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 176 | </style>""" |
| 177 | ) |
| 178 | ) |
David Spickett | 55449c6 | 2021-12-13 12:57:33 +0000 | [diff] [blame] | 179 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 180 | column_titles = [ |
| 181 | "Buildbot", |
| 182 | "Status", |
| 183 | "T Since", |
| 184 | "Duration", |
Antoine Moynault | 0cbe02e | 2023-06-21 08:13:57 +0000 | [diff] [blame] | 185 | "Latest", |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 186 | "Failing steps", |
| 187 | "Build In Progress", |
Antoine Moynault | 0cbe02e | 2023-06-21 08:13:57 +0000 | [diff] [blame] | 188 | "1st Failing", |
| 189 | "Failing Since", |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 190 | ] |
| 191 | num_columns = len(column_titles) |
David Spickett | e44441b | 2023-06-12 12:23:28 +0100 | [diff] [blame] | 192 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 193 | # The first table should also say when this was generated. |
| 194 | # If we were to put this in its own header only table, it would |
| 195 | # not align with the rest because it has no content. |
| 196 | first = True |
David Spickett | e44441b | 2023-06-12 12:23:28 +0100 | [diff] [blame] | 197 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 198 | # Dump all servers / bots |
| 199 | for server in filter(not_ignored, config): |
| 200 | with Table(temp) as table: |
| 201 | table.Border(0).Cellspacing(1).Cellpadding(2) |
David Spickett | ec94cc2 | 2021-12-13 13:16:05 +0000 | [diff] [blame] | 202 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 203 | table.AddRow().AddCell().Colspan(num_columns) |
David Spickett | e44441b | 2023-06-12 12:23:28 +0100 | [diff] [blame] | 204 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 205 | if first: |
| 206 | table.AddRow().AddHeader( |
| 207 | "Generated {} ({})".format( |
| 208 | datetime.today().ctime(), time.tzname[time.daylight] |
| 209 | ) |
| 210 | ).Colspan(num_columns) |
| 211 | table.AddRow().AddCell().Colspan(num_columns) |
| 212 | first = False |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 213 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 214 | table.AddRow().AddHeader(server["name"]).Colspan(num_columns) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 215 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 216 | for builder in server["builders"]: |
| 217 | table.AddRow().AddCell().Colspan(num_columns) |
| 218 | table.AddRow().AddHeader(builder["name"]).Colspan(num_columns) |
| 219 | title_row = table.AddRow() |
| 220 | for title in column_titles: |
| 221 | title_row.AddHeader(title) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 222 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 223 | table.BeginBody() |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 224 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 225 | for bot in builder["bots"]: |
| 226 | logging.debug("Writing out status for {}".format(bot["name"])) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 227 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 228 | row = table.AddRow() |
| 229 | base_url = server["base_url"] |
| 230 | try: |
| 231 | status = bots_status[(base_url, bot["name"])] |
| 232 | except KeyError: |
| 233 | row.AddCell("{} is offline!".format(bot["name"])).Colspan( |
| 234 | num_columns |
| 235 | ) |
| 236 | continue |
| 237 | else: |
| 238 | if not status.get("valid", True): |
| 239 | row.AddCell( |
| 240 | "Could not read status for {}!".format(bot["name"]) |
| 241 | ).Colspan(num_columns) |
| 242 | continue |
David Spickett | f2c82dd | 2021-06-24 10:01:33 +0100 | [diff] [blame] | 243 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 244 | row.AddCell( |
| 245 | "<a href='{}'>{}</a>".format(status["builder_url"], bot["name"]) |
| 246 | ) |
Antoine Moynault | 0cbe02e | 2023-06-21 08:13:57 +0000 | [diff] [blame] | 247 | |
| 248 | status_cell = row.AddCell() |
| 249 | if status["fail"]: |
| 250 | status_cell.Style("color:red").Content("FAIL") |
| 251 | else: |
| 252 | status_cell.Style("color:green").Content("PASS") |
David Spickett | 187f796 | 2022-02-09 12:35:00 +0000 | [diff] [blame] | 253 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 254 | time_since_cell = row.AddCell() |
| 255 | if "time_since" in status: |
| 256 | time_since = status["time_since"] |
| 257 | # No build should be taking more than a day |
| 258 | if time_since > timedelta(hours=24): |
Antoine Moynault | 0cbe02e | 2023-06-21 08:13:57 +0000 | [diff] [blame] | 259 | time_since_cell.Style("color:red") |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 260 | time_since_cell.Content(time_since) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 261 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 262 | duration_cell = row.AddCell() |
| 263 | if "duration" in status: |
| 264 | duration_cell.Content(status["duration"]) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 265 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 266 | number_cell = row.AddCell() |
| 267 | if "number" in status: |
| 268 | number_cell.Content( |
| 269 | "<a href='{}'>{}</a>".format( |
| 270 | status["build_url"], status["number"] |
| 271 | ) |
| 272 | ) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 273 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 274 | steps_cell = row.AddCell() |
| 275 | if "steps" in status and status["steps"]: |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 276 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 277 | def render_step(name, result): |
| 278 | return "<font color='{}'>{}</font>".format( |
| 279 | RESULT_COLORS[result], name |
| 280 | ) |
David Spickett | 82c94b2 | 2023-06-12 16:18:33 +0100 | [diff] [blame] | 281 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 282 | step_list = ", ".join( |
| 283 | render_step(name, result) |
| 284 | for name, result in status["steps"] |
| 285 | ) |
| 286 | steps_cell.Style("text-align:center").Content(step_list) |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 287 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 288 | next_in_progress_cell = row.AddCell() |
| 289 | if "next_in_progress" in status: |
David Spickett | f850243 | 2023-07-07 15:52:26 +0100 | [diff] [blame] | 290 | next_build = status["next_in_progress"] |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 291 | next_in_progress_cell.Content( |
David Spickett | f850243 | 2023-07-07 15:52:26 +0100 | [diff] [blame] | 292 | "No" |
| 293 | if next_build is None |
| 294 | else "<a href='{}'>Yes</a>".format(next_build) |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 295 | ) |
| 296 | |
Antoine Moynault | 0cbe02e | 2023-06-21 08:13:57 +0000 | [diff] [blame] | 297 | first_fail_cell = row.AddCell() |
| 298 | if "first_fail_number" in status: |
| 299 | first_fail_cell.Content( |
| 300 | "<a href='{}'>{}</a>".format( |
| 301 | status["first_fail_url"], status["first_fail_number"] |
| 302 | ) |
| 303 | ) |
| 304 | |
| 305 | fail_since_cell = row.AddCell() |
| 306 | if "fail_since" in status: |
| 307 | fail_since = status["fail_since"] |
| 308 | # No build should fail for more than a day |
| 309 | if fail_since > timedelta(hours=24): |
| 310 | fail_since_cell.Style("color:red") |
| 311 | fail_since_cell.Content(fail_since) |
| 312 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 313 | table.EndBody() |
| 314 | |
| 315 | # Move temp to main (atomic change) |
| 316 | temp.close() |
| 317 | shutil.move(temp.name, output_file) |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 318 | |
| 319 | |
| 320 | if __name__ == "__main__": |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 321 | parser = argparse.ArgumentParser() |
| 322 | parser.add_argument( |
| 323 | "-d", dest="debug", action="store_true", help="show debug log messages" |
| 324 | ) |
David Spickett | 55cc06c | 2023-07-26 15:29:20 +0100 | [diff] [blame] | 325 | parser.add_argument( |
| 326 | "--cachefile", |
| 327 | required=False, |
| 328 | help="Location of bot status data cache file (a pickled Python object). If it exists use it, " |
| 329 | "if it does not, read the status from the network and write it to this path.", |
| 330 | ) |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 331 | parser.add_argument("config_file", help="Bots description in JSON format") |
| 332 | parser.add_argument("output_file", help="output HTML path") |
| 333 | args = parser.parse_args() |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 334 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 335 | if args.debug: |
| 336 | logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 337 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 338 | try: |
| 339 | with open(args.config_file, "r") as f: |
| 340 | config = json.load(f) |
| 341 | except IOError as e: |
| 342 | print("error: failed to read {} config file: {}".format(args.config_file, e)) |
| 343 | sys.exit(os.EX_CONFIG) |
Adhemerval Zanella | d3e8c48 | 2020-10-12 11:31:48 -0300 | [diff] [blame] | 344 | |
David Spickett | 55cc06c | 2023-07-26 15:29:20 +0100 | [diff] [blame] | 345 | status = None |
| 346 | if args.cachefile and os.path.exists(args.cachefile): |
| 347 | logging.debug("Using cache file {}".format(args.cachefile)) |
| 348 | with open(args.cachefile, "rb") as f: |
| 349 | status = pickle.load(f) |
| 350 | else: |
| 351 | status = get_buildbot_bots_status(config) |
David Spickett | 55cc06c | 2023-07-26 15:29:20 +0100 | [diff] [blame] | 352 | if args.cachefile: |
| 353 | logging.debug("Writing status to cache file {}".format(args.cachefile)) |
| 354 | with open(args.cachefile, "wb") as f: |
| 355 | pickle.dump(status, f) |
| 356 | |
David Spickett | 4f932d1 | 2023-06-13 12:32:06 +0100 | [diff] [blame] | 357 | write_bot_status(config, args.output_file, status) |