mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
Dashboard improvements
This commit is contained in:
92
dashboard.py
92
dashboard.py
@@ -1,28 +1,24 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
|
||||||
import os.path
|
import os.path
|
||||||
import socket
|
import socket
|
||||||
import time
|
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import psutil
|
|
||||||
import requests
|
import requests
|
||||||
import click
|
|
||||||
from rich import box
|
from rich import box
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
from rich.layout import Layout
|
||||||
from rich.live import Live
|
from rich.live import Live
|
||||||
|
from rich.panel import Panel
|
||||||
from rich.table import Column
|
from rich.table import Column
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from rich.layout import Layout
|
|
||||||
from rich.panel import Panel
|
|
||||||
from rich.tree import Tree
|
from rich.tree import Tree
|
||||||
|
|
||||||
import zordon_server
|
import zordon_server
|
||||||
from zordon_server import RenderStatus
|
from zordon_server import RenderStatus, string_to_status
|
||||||
from zordon_server import string_to_status
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The RenderDashboard is designed to be run on a remote machine or on the local server
|
The RenderDashboard is designed to be run on a remote machine or on the local server
|
||||||
@@ -36,6 +32,8 @@ status_colors = {RenderStatus.ERROR: "red", RenderStatus.CANCELLED: 'orange1', R
|
|||||||
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
||||||
RenderStatus.COMPLETED, RenderStatus.CANCELLED]
|
RenderStatus.COMPLETED, RenderStatus.CANCELLED]
|
||||||
|
|
||||||
|
renderer_colors = {'ffmpeg': '[magenta]', 'Blender': '[orange1]', 'aerender':'[purple'}
|
||||||
|
|
||||||
local_hostname = socket.gethostname()
|
local_hostname = socket.gethostname()
|
||||||
|
|
||||||
|
|
||||||
@@ -46,15 +44,19 @@ def status_string_to_color(status_string):
|
|||||||
|
|
||||||
|
|
||||||
def sorted_jobs(all_jobs):
|
def sorted_jobs(all_jobs):
|
||||||
# sorted_jobs = []
|
|
||||||
# if all_jobs:
|
sort_by_date = True
|
||||||
# for status_category in categories:
|
if not sort_by_date:
|
||||||
# found_jobs = [x for x in all_jobs if x['status'] == status_category.value]
|
sorted_job_list = []
|
||||||
# if found_jobs:
|
if all_jobs:
|
||||||
# sorted_found_jobs = sorted(found_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True)
|
for status_category in categories:
|
||||||
# sorted_jobs.extend(sorted_found_jobs)
|
found_jobs = [x for x in all_jobs if x['status'] == status_category.value]
|
||||||
sorted_jobs = sorted(all_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True)
|
if found_jobs:
|
||||||
return sorted_jobs
|
sorted_found_jobs = sorted(found_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True)
|
||||||
|
sorted_job_list.extend(sorted_found_jobs)
|
||||||
|
else:
|
||||||
|
sorted_job_list = sorted(all_jobs, key=lambda d: datetime.datetime.fromisoformat(d['date_created']), reverse=True)
|
||||||
|
return sorted_job_list
|
||||||
|
|
||||||
|
|
||||||
def create_node_tree(all_server_data) -> Tree:
|
def create_node_tree(all_server_data) -> Tree:
|
||||||
@@ -69,29 +71,30 @@ def create_node_tree(all_server_data) -> Tree:
|
|||||||
|
|
||||||
node_tree = Tree(node_tree_text)
|
node_tree = Tree(node_tree_text)
|
||||||
|
|
||||||
stats_text = f"CPU: {server_data['status']['cpu_percent']}% | RAM: {server_data['status']['memory_percent']}% | " \
|
stats_text = f"CPU: [yellow]{server_data['status']['cpu_percent']}% [default]| RAM: " \
|
||||||
f"Cores: {server_data['status']['cpu_count']} | {server_data['status']['platform'].split('-')[0]}"
|
f"[yellow]{server_data['status']['memory_percent']}% [default]| Cores: " \
|
||||||
|
f"[yellow]{server_data['status']['cpu_count']} [default]| " \
|
||||||
|
f"{server_data['status']['platform'].split('-')[0]}"
|
||||||
|
|
||||||
node_tree.add(Tree(stats_text))
|
node_tree.add(Tree(stats_text))
|
||||||
|
|
||||||
running_jobs = [job for job in server_data['jobs'] if job['status'] == 'running']
|
running_jobs = [job for job in server_data['jobs'] if job['status'] == 'running']
|
||||||
not_started = [job for job in server_data['jobs'] if job['status'] == 'not_started']
|
not_started = [job for job in server_data['jobs'] if job['status'] == 'not_started']
|
||||||
scheduled = [job for job in server_data['jobs'] if job['status'] == 'scheduled']
|
scheduled = [job for job in server_data['jobs'] if job['status'] == 'scheduled']
|
||||||
|
jobs_to_display = running_jobs + not_started + scheduled
|
||||||
|
|
||||||
jobs_tree = Tree(f"Running: [green]{len(running_jobs)} [default]| Queued: [cyan]{len(not_started)}"
|
jobs_tree = Tree(f"Running: [green]{len(running_jobs)} [default]| Queued: [cyan]{len(not_started)}"
|
||||||
f"[default] | Scheduled: [cyan]{len(scheduled)}")
|
f"[default] | Scheduled: [cyan]{len(scheduled)}")
|
||||||
|
|
||||||
if running_jobs or not_started or scheduled:
|
for job in jobs_to_display:
|
||||||
for job in running_jobs:
|
renderer = f"{renderer_colors[job['renderer']]}{job['renderer']}[default]"
|
||||||
filename = os.path.basename(job['render']['input']).split('.')[0]
|
filename = os.path.basename(job['render']['input']).split('.')[0]
|
||||||
jobs_tree.add(f"[bold]{filename} ({job['id']}) - {status_string_to_color(job['status'])}{(float(job['render']['percent_complete']) * 100):.1f}%")
|
if job['status'] == 'running':
|
||||||
for job in not_started:
|
jobs_tree.add(f"[bold]{renderer} {filename} ({job['id']}) - {status_string_to_color(job['status'])}{(float(job['render']['percent_complete']) * 100):.1f}%")
|
||||||
filename = os.path.basename(job['render']['input']).split('.')[0]
|
else:
|
||||||
jobs_tree.add(f"{filename} ({job['id']}) - {status_string_to_color(job['status'])}{job['status'].title()}")
|
jobs_tree.add(f"{filename} ({job['id']}) - {status_string_to_color(job['status'])}{job['status'].title()}")
|
||||||
for job in scheduled:
|
|
||||||
filename = os.path.basename(job['render']['input']).split('.')[0]
|
if not jobs_to_display:
|
||||||
jobs_tree.add(f"{filename} ({job['id']}) - {status_string_to_color(job['status'])}{job['status'].title()}")
|
|
||||||
else:
|
|
||||||
jobs_tree.add("[italic]No running jobs")
|
jobs_tree.add("[italic]No running jobs")
|
||||||
|
|
||||||
node_tree.add(jobs_tree)
|
node_tree.add(jobs_tree)
|
||||||
@@ -102,13 +105,13 @@ def create_node_tree(all_server_data) -> Tree:
|
|||||||
def create_jobs_table(all_server_data) -> Table:
|
def create_jobs_table(all_server_data) -> Table:
|
||||||
table = Table("ID", "Project", "Output", "Renderer", Column(header="Priority", justify="center"),
|
table = Table("ID", "Project", "Output", "Renderer", Column(header="Priority", justify="center"),
|
||||||
Column(header="Status", justify="center"), Column(header="Time Elapsed", justify="right"),
|
Column(header="Status", justify="center"), Column(header="Time Elapsed", justify="right"),
|
||||||
Column(header="# Frames", justify="right"), "Node", show_lines=True,
|
Column(header="# Frames", justify="right"), "Client", show_lines=True,
|
||||||
box=box.HEAVY_HEAD)
|
box=box.HEAVY_HEAD)
|
||||||
|
|
||||||
all_jobs = []
|
all_jobs = []
|
||||||
for server_name, server_data in all_server_data['servers'].items():
|
for server_name, server_data in all_server_data['servers'].items():
|
||||||
for job in server_data['jobs']:
|
for job in server_data['jobs']:
|
||||||
job['node'] = server_name
|
#todo: clean this up
|
||||||
all_jobs.append(job)
|
all_jobs.append(job)
|
||||||
|
|
||||||
all_jobs = sorted_jobs(all_jobs)
|
all_jobs = sorted_jobs(all_jobs)
|
||||||
@@ -117,7 +120,10 @@ def create_jobs_table(all_server_data) -> Table:
|
|||||||
|
|
||||||
job_status = string_to_status(job['status'])
|
job_status = string_to_status(job['status'])
|
||||||
job_color = '[{}]'.format(status_colors[job_status])
|
job_color = '[{}]'.format(status_colors[job_status])
|
||||||
job_text = f"{job_color}" + job_status.value
|
job_text = f"{job_color}" + job_status.value.title()
|
||||||
|
|
||||||
|
if job_status == RenderStatus.ERROR and job['render']['errors']:
|
||||||
|
job_text = job_text + "\n" + "\n".join(job['render']['errors'])
|
||||||
|
|
||||||
elapsed_time = job['render'].get('time_elapsed', 'unknown')
|
elapsed_time = job['render'].get('time_elapsed', 'unknown')
|
||||||
|
|
||||||
@@ -136,8 +142,9 @@ def create_jobs_table(all_server_data) -> Table:
|
|||||||
# Priority
|
# Priority
|
||||||
priority_color = ["red", "yellow", "cyan"][(job['priority'] - 1)]
|
priority_color = ["red", "yellow", "cyan"][(job['priority'] - 1)]
|
||||||
|
|
||||||
node_title = ("[yellow]" if job['node'] == local_hostname else "[magenta]") + job['node']
|
client_name = job['client'] or 'unknown'
|
||||||
renderer_colors = {'ffmpeg': '[magenta]', 'Blender': '[orange1]'}
|
client_colors = {'unknown': '[red]', local_hostname: '[yellow]'}
|
||||||
|
client_title = client_colors.get(client_name, '[magenta]') + client_name
|
||||||
|
|
||||||
table.add_row(
|
table.add_row(
|
||||||
job['id'],
|
job['id'],
|
||||||
@@ -148,7 +155,7 @@ def create_jobs_table(all_server_data) -> Table:
|
|||||||
job_text,
|
job_text,
|
||||||
elapsed_time,
|
elapsed_time,
|
||||||
str(max(int(job['render']['total_frames']), 1)),
|
str(max(int(job['render']['total_frames']), 1)),
|
||||||
node_title
|
client_title
|
||||||
)
|
)
|
||||||
|
|
||||||
return table
|
return table
|
||||||
@@ -179,7 +186,6 @@ class RenderDashboard:
|
|||||||
return req.json()
|
return req.json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
# print(f"Exception fetching server data: {e}")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_jobs(self):
|
def get_jobs(self):
|
||||||
@@ -216,20 +222,20 @@ def my_callback(inp):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
server_ip = input("Enter server IP or None for local: ") or local_hostname
|
get_server_ip = input("Enter server IP or None for local: ") or local_hostname
|
||||||
|
|
||||||
client = RenderDashboard(server_ip, "8080")
|
client = RenderDashboard(get_server_ip, "8080")
|
||||||
|
|
||||||
if not client.connect():
|
if not client.connect():
|
||||||
if server_ip == local_hostname:
|
if client.server_ip == local_hostname:
|
||||||
start_server = input("Local server not running. Start server? (y/n) ")
|
start_server = input("Local server not running. Start server? (y/n) ")
|
||||||
if start_server and start_server[0].lower() == "y":
|
if start_server and start_server[0].lower() == "y":
|
||||||
# Startup the local server
|
# Startup the local server
|
||||||
zordon_server.start_server(background_thread=True)
|
zordon_server.RenderServer.start(background_thread=True)
|
||||||
test = client.connect()
|
test = client.connect()
|
||||||
print(f"connected? {test}")
|
print(f"connected? {test}")
|
||||||
else:
|
else:
|
||||||
print(f"\nUnable to connect to server: {server_ip}")
|
print(f"\nUnable to connect to server: {client.server_ip}")
|
||||||
print("\nVerify IP address is correct and server is running")
|
print("\nVerify IP address is correct and server is running")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@@ -256,8 +262,8 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Server connection header
|
# Server connection header
|
||||||
header_text = Text(f"Connected to server: ")
|
header_text = Text(f"Connected to server: ")
|
||||||
header_text.append(f"{server_ip} ", style="green")
|
header_text.append(f"{client.server_ip} ", style="green")
|
||||||
if server_ip == local_hostname:
|
if client.server_ip == local_hostname:
|
||||||
header_text.append("(This Computer)", style="yellow")
|
header_text.append("(This Computer)", style="yellow")
|
||||||
else:
|
else:
|
||||||
header_text.append("(Remote)", style="magenta")
|
header_text.append("(Remote)", style="magenta")
|
||||||
|
|||||||
Reference in New Issue
Block a user