-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
FROM registry.fedoraproject.org/fedora-minimal:39 | ||
|
||
RUN microdnf install -y pip gnuplot && microdnf clean all | ||
RUN pip install requests && rm -rf /root/.cache/pip | ||
|
||
COPY gnuplot-script . | ||
COPY tasks-cli.py . | ||
|
||
CMD ["python", "tasks-cli.py"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
* building | ||
``` | ||
podman build -t tasks-cli:latest . | ||
``` | ||
|
||
* running | ||
``` | ||
podman run -it --rm tasks-cli | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
set xlabel "Values" | ||
set ylabel "Frequency" | ||
set title "Histogram of Data Values" | ||
set output "/tmp/tasks.png" | ||
|
||
|
||
#set xdata time | ||
#set timefmt "%Y-%m-%dT%H:%M:%S.%zZ" | ||
|
||
#set style data lines | ||
set style data histogram | ||
set style histogram rowstacked | ||
set boxwidth 0.75 | ||
#set style histogram cluster gap 1 | ||
set style fill solid 1.0 border -1.0 # Fill style for the boxes | ||
|
||
# Fine control of the major (labelled) tics on the x axis | ||
set xtics rotate by -90 | ||
|
||
#plot "/tmp/a" u 1:2 | ||
plot filename using 2:xtic(1) ti 'runing' lt rgb 'green', '' using 3 ti 'waiting blocked' lt rgb 'red', '' using 4 ti 'waiting unblocked' lt rgb 'blue' | ||
|
||
set grid ytics lt rgb "gray" | ||
pause -1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import argparse | ||
import json | ||
import os | ||
import requests | ||
import subprocess | ||
|
||
from datetime import datetime, timedelta | ||
from types import SimpleNamespace | ||
|
||
|
||
DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" | ||
TZ = timedelta(hours=3) | ||
TASKS_ENDPOINT = "/api/pulp/admin/tasks/?fields=started_at&" | ||
|
||
QUERY_TYPES = SimpleNamespace( | ||
RUNNING="running", | ||
WAITING_UNBLOCKED="waiting_unblocked", | ||
WAITING_BLOCKED="waiting_blocked", | ||
) | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"-b", | ||
"--base_address", | ||
help="Pulp hostname address. For example: http://pulp-service:5001", | ||
default="http://pulp-api-ephemeral-dden7g.apps.crc-eph.r9lp.p1.openshiftapps.com", | ||
) | ||
parser.add_argument( | ||
"-u", "--username", help="Pulp user to run the API requests. [DEFAULT: admin]", default="admin" | ||
) | ||
parser.add_argument( | ||
"-p", | ||
"--password", | ||
help="Password for Pulp user. [DEFAULT: password]", | ||
default="X3fmXzf1ud0Dw1Kb4SbQ5f1IhWZm5VAirGXWVXTNV64=", | ||
) | ||
parser.add_argument("-c", "--certificate", help="Certificate to authenticate to Pulp API.") | ||
parser.add_argument("-k", "--key", help="Private key for the certificate authentication.") | ||
parser.add_argument( | ||
"--period", | ||
help="Period, in hours, to check the tasks. For example, for the last 24 hours: --period=24 [DEFAULT: 24]", | ||
type=int, | ||
default=6, | ||
) | ||
parser.add_argument( | ||
"--bucket_size", | ||
help="Bucket size, in seconds. For example, for a 30 seconds bucket: --bucket-size=30 [DEFAULT: 3600]", | ||
type=int, | ||
default=1800, | ||
) | ||
parser.add_argument( | ||
"-o", | ||
"--output", | ||
help="Output file. [DEFAULT: /tmp/pulp_tasks.out]", | ||
type=str, | ||
default="/tmp/pulp_tasks.out", | ||
) | ||
|
||
args = parser.parse_args() | ||
|
||
base_addr = args.base_address | ||
username = args.username | ||
password = args.password | ||
period_in_hours = args.period | ||
bucket_size_in_seconds = args.bucket_size | ||
output_file = args.output | ||
|
||
|
||
def run(): | ||
|
||
datetime_now = datetime.now() + TZ | ||
query_date_time = datetime_now - timedelta(hours=period_in_hours) | ||
start_date = query_date_time.strftime(DATETIME_FORMAT) | ||
data = initialize_response_structure(period_in_hours, bucket_size_in_seconds, query_date_time) | ||
|
||
for task_state in QUERY_TYPES.__dict__.values(): | ||
tasks = get_tasks(start_date, task_state) | ||
make_buckets( | ||
tasks, bucket_size_in_seconds, query_date_time, period_in_hours, task_state, data | ||
) | ||
|
||
# [TODO]: remove the following!!!! | ||
# overwriting the first column for tests | ||
tasks = get_tasks(start_date) | ||
make_buckets( | ||
tasks, bucket_size_in_seconds, query_date_time, period_in_hours, QUERY_TYPES.RUNNING, data | ||
) | ||
|
||
write_to_file(data) | ||
p = subprocess.Popen("gnuplot -e \"filename='"+output_file+"'\" -c gnuplot-script", shell = True) | ||
os.waitpid(p.pid, 0) | ||
|
||
def write_to_file(data): | ||
with open(output_file, "w") as f: | ||
for key in data: | ||
print( | ||
key, | ||
data[key][QUERY_TYPES.RUNNING], | ||
data[key][QUERY_TYPES.WAITING_BLOCKED], | ||
data[key][QUERY_TYPES.WAITING_UNBLOCKED], | ||
) | ||
f.write( | ||
key | ||
+ " " | ||
+ str(data[key][QUERY_TYPES.RUNNING]) | ||
+ " " | ||
+ str(data[key][QUERY_TYPES.WAITING_BLOCKED]) | ||
+ " " | ||
+ str(data[key][QUERY_TYPES.WAITING_UNBLOCKED]) | ||
+ "\n" | ||
) | ||
|
||
def get_tasks(start_date, query_type=None): | ||
url = base_addr + TASKS_ENDPOINT + "started_at__gte=" + start_date | ||
if query_type == QUERY_TYPES.RUNNING: | ||
url = running_tasks_url(start_date) | ||
elif query_type == QUERY_TYPES.WAITING_UNBLOCKED: | ||
url = tasks_in_waiting_state_and_unblocked_url(False, start_date) | ||
elif query_type == QUERY_TYPES.WAITING_BLOCKED: | ||
url = tasks_in_waiting_state_and_unblocked_url(True, start_date) | ||
|
||
response = requests.get(url, auth=(username, password)) | ||
response_json = json.loads(response.text) | ||
|
||
tasks_found_datetime = [] | ||
if response_json.get("results"): | ||
for result in response_json["results"]: | ||
tasks_found_datetime.append(result["started_at"]) | ||
|
||
return tasks_found_datetime | ||
|
||
|
||
def initialize_response_structure(period, bucket_size, query_date_time): | ||
data = {} | ||
total_seconds = timedelta(hours=period).total_seconds() | ||
number_of_intervals = int(total_seconds // bucket_size) | ||
|
||
# Create a list of bucket start times | ||
bucket_starts = [ | ||
query_date_time + timedelta(seconds=i * bucket_size) for i in range(number_of_intervals) | ||
] | ||
|
||
# Initialize buckets | ||
for start_time in bucket_starts: | ||
data[start_time.strftime(DATETIME_FORMAT)] = {} | ||
for task_state in QUERY_TYPES.__dict__.values(): | ||
data[start_time.strftime(DATETIME_FORMAT)][task_state] = 0 | ||
|
||
return data | ||
|
||
|
||
def make_buckets(tasks_found_datetime, bucket_size, query_date_time, period, query_type, data): | ||
total_seconds = timedelta(hours=period).total_seconds() | ||
number_of_intervals = int(total_seconds // bucket_size) | ||
|
||
# Count tasks in each bucket | ||
for task_datetime_str in tasks_found_datetime: | ||
task_datetime = datetime.strptime(task_datetime_str, DATETIME_FORMAT) | ||
|
||
# Find the appropriate bucket for the task | ||
for i in range(number_of_intervals): | ||
start_time = query_date_time + timedelta(seconds=i * bucket_size) | ||
end_time = start_time + timedelta(seconds=bucket_size) | ||
|
||
if start_time < task_datetime < end_time: | ||
data[start_time.strftime(DATETIME_FORMAT)][query_type] += 1 | ||
break # Task is counted, no need to check further | ||
|
||
return data | ||
|
||
|
||
def running_tasks_url(start_date): | ||
return base_addr + TASKS_ENDPOINT + "started_at__gte=" + start_date + "&state=running" | ||
|
||
|
||
def tasks_in_waiting_state_and_unblocked_url(unblocked_null, start_date): | ||
return ( | ||
base_addr | ||
+ TASKS_ENDPOINT | ||
+ "started_at__gte=" | ||
+ start_date | ||
+ "&state=waiting&unblocked_at__isnull=" | ||
+ str(unblocked_null) | ||
) | ||
|
||
|
||
run() |