|
|
@@ -0,0 +1,167 @@ |
|
|
|
#!/bin/bash |
|
|
|
columns=("JOBID" "URL" "USER" "PIPENICK" "QUEUED" "STARTED" "LAST ACTIVE") # Duplicated in Python code! |
|
|
|
|
|
|
|
function valid_column { |
|
|
|
local candidate="$1" |
|
|
|
local column |
|
|
|
for column in "${columns[@]}" |
|
|
|
do |
|
|
|
[[ "${candidate}" == "${column}" ]] && return 0 |
|
|
|
done |
|
|
|
return 1 |
|
|
|
} |
|
|
|
|
|
|
|
sortcolumns=() |
|
|
|
filter= |
|
|
|
nocolours= |
|
|
|
notable= |
|
|
|
while [[ $# -gt 0 ]] |
|
|
|
do |
|
|
|
if [[ "$1" == "--help" || "$1" == "-h" ]] |
|
|
|
then |
|
|
|
echo "Usage: archivebot-jobs [--help|-h] [(--sort|-s) COLUMN] [(--filter|-f) COLUMN=VALUE] [--no-colours|--no-colors] [--no-table]" >&2 |
|
|
|
echo "Prints a table of current AB jobs" >&2 |
|
|
|
echo "Options:" >&2 |
|
|
|
echo " --help, -h: Show this message and exit." >&2 |
|
|
|
echo " --sort COLUMN: Sort the table by a column. This can be used multiple times to refine the sorting." >&2 |
|
|
|
echo " --filter COLUMN=VALUE: Filter the table for rows where a COLUMN has a certain VALUE. If specified multiple times, only the last value is used." >&2 |
|
|
|
echo " --no-colours: Don't colourise the last activity column if it's been a while." >&2 |
|
|
|
echo " --no-table: Raw output without feeding through column(1); columns are separated by tabs." >&2 |
|
|
|
echo "The COLUMNs are the names of each column, printed in capital letters in the first line of the output." >&2 |
|
|
|
exit 0 |
|
|
|
elif [[ "$1" == "--sort" || "$1" == "-s" ]] |
|
|
|
then |
|
|
|
sortcolumns+=("$2") |
|
|
|
shift |
|
|
|
elif [[ "$1" == "--filter" || "$1" == "-f" ]] |
|
|
|
then |
|
|
|
filter="$2" |
|
|
|
shift |
|
|
|
elif [[ "$1" == "--no-colours" || "$1" == "--no-colors" ]] |
|
|
|
then |
|
|
|
nocolours=1 |
|
|
|
elif [[ "$1" == "--no-table" ]] |
|
|
|
then |
|
|
|
notable=1 |
|
|
|
else |
|
|
|
echo "Unknown option: $1" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
shift |
|
|
|
done |
|
|
|
|
|
|
|
# Validate sortcolumns and filter |
|
|
|
if [[ "${filter}" ]] |
|
|
|
then |
|
|
|
if [[ ! "${filter}" == *=* ]] |
|
|
|
then |
|
|
|
echo "Invalid filter: ${filter}" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
if [[ "${filter}" == *$'\n'* ]] |
|
|
|
then |
|
|
|
echo "Invalid filter: newlines not allowed" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
column="${filter%%=*}" |
|
|
|
if ! valid_column "${column}" |
|
|
|
then |
|
|
|
echo "Invalid filter column: ${column}" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
fi |
|
|
|
if [[ ${#sortcolumns[@]} -gt 0 ]] |
|
|
|
then |
|
|
|
for column in "${sortcolumns[@]}" |
|
|
|
do |
|
|
|
if ! valid_column "${column}" |
|
|
|
then |
|
|
|
echo "Invalid sort column: ${column}" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
done |
|
|
|
else |
|
|
|
# Default sort order |
|
|
|
sortcolumns+=("JOBID") |
|
|
|
fi |
|
|
|
|
|
|
|
if [[ "${notable}" ]] |
|
|
|
then |
|
|
|
column=("cat") |
|
|
|
else |
|
|
|
column=("column" "-t" $'-s\t') |
|
|
|
fi |
|
|
|
|
|
|
|
jobdata="$(curl -s -H "Accept: application/json" "http://dashboard.at.ninjawedding.org/logs/recent?count=1" 2>/dev/null)" |
|
|
|
pipelinedata="$(curl -s -H "Accept: application/json" "http://dashboard.at.ninjawedding.org/pipelines" 2>/dev/null)" |
|
|
|
|
|
|
|
if [[ -z "${jobdata}" || -z "${pipelinedata}" ]] |
|
|
|
then |
|
|
|
echo "Error retrieving job or pipeline data" >&2 |
|
|
|
exit 1 |
|
|
|
fi |
|
|
|
|
|
|
|
{ echo "${jobdata}"; echo "${pipelinedata}"; echo "${filter}"; } | python3 -c \ |
|
|
|
' |
|
|
|
if True: # For sensible indentation |
|
|
|
import json |
|
|
|
import sys |
|
|
|
import time |
|
|
|
|
|
|
|
def time_ago(diff): |
|
|
|
if diff <= 0: |
|
|
|
return "now" |
|
|
|
elif diff < 60: |
|
|
|
return "<1 min ago" |
|
|
|
elif diff < 86400: |
|
|
|
return (f"{diff // 3600:.0f}h " if diff >= 3600 else "") + f"{(diff % 3600) // 60:.0f}mn ago" |
|
|
|
else: |
|
|
|
return f"{diff // 86400:.0f}d {(diff % 86400) // 3600:.0f}h ago" |
|
|
|
|
|
|
|
def coloured_time_ago(diff): |
|
|
|
if diff >= 300: |
|
|
|
return "\x1b[0;31m" + time_ago(diff) + "\x1b[0m" |
|
|
|
else: |
|
|
|
return time_ago(diff) |
|
|
|
|
|
|
|
jobdata = json.loads(sys.stdin.readline()) |
|
|
|
pipelinedata = json.loads(sys.stdin.readline()) |
|
|
|
filter = sys.stdin.readline().strip() |
|
|
|
|
|
|
|
pipelines = {p["id"]: p["nickname"] for p in pipelinedata["pipelines"]} |
|
|
|
|
|
|
|
columns = ("JOBID", "URL", "USER", "PIPENICK", "QUEUED", "STARTED", "LAST ACTIVE") # Duplicated in Bash code! |
|
|
|
jobs = [] |
|
|
|
currentTime = time.time() |
|
|
|
for j in jobdata: |
|
|
|
jobs.append([ |
|
|
|
j["job_data"]["ident"], |
|
|
|
j["job_data"]["url"], |
|
|
|
j["job_data"]["started_by"], |
|
|
|
pipelines[j["job_data"]["pipeline_id"]] if j["job_data"]["pipeline_id"] in pipelines else "unknown", |
|
|
|
currentTime - j["job_data"]["queued_at"], |
|
|
|
currentTime - j["job_data"]["started_at"], |
|
|
|
currentTime - j["ts"], |
|
|
|
]) |
|
|
|
|
|
|
|
# Filter |
|
|
|
if filter: |
|
|
|
column, value = filter.split("=", 1) |
|
|
|
assert column in columns |
|
|
|
columnIdx = columns.index(column) |
|
|
|
jobs = [job for job in jobs if job[columnIdx] == value] |
|
|
|
|
|
|
|
# Sort |
|
|
|
sortColumns = ('"$(printf "'%s', " "${sortcolumns[@]}")"') |
|
|
|
assert all(column in columns for column in sortColumns) |
|
|
|
sortColumnIdxs = tuple(columns.index(column) for column in sortColumns) |
|
|
|
jobs = sorted(jobs, key = lambda job: tuple(job[columnIdx] for columnIdx in sortColumnIdxs)) |
|
|
|
|
|
|
|
# Print |
|
|
|
print("\t".join(columns)) |
|
|
|
for job in jobs: |
|
|
|
job[4] = time_ago(job[4]) |
|
|
|
job[5] = time_ago(job[5]) |
|
|
|
job[6] = (coloured_time_ago if not "'${nocolours}'" else time_ago)(job[6]) |
|
|
|
print("\t".join(job)) |
|
|
|
' | "${column[@]}" |