Merge pull request #7 from blw1138/zeroconf

Zeroconf
This commit is contained in:
2023-06-02 16:11:19 -05:00
committed by GitHub
7 changed files with 146 additions and 47 deletions

0
lib/client/__init__.py Normal file
View File

View File

@@ -5,11 +5,13 @@ import tkinter as tk
import threading
import time
import socket
import os
import os, sys
from tkinter import ttk, messagebox
from PIL import Image, ImageTk
from new_job_window import NewJobWindow
from server_proxy import RenderServerProxy
sys.path.append("../")
from lib.server.zeroconf_server import ZeroconfServer
def request_data(server_ip, payload, server_port=8080, timeout=2):
@@ -44,15 +46,17 @@ class ZordonClient:
def __init__(self):
servers = available_servers()
# Create a Treeview widget
self.root = tk.Tk()
self.root.title("Zordon Render Client")
self.local_host = socket.gethostname()
self.server_proxy = RenderServerProxy(hostname=servers[0])
self.server_proxy = RenderServerProxy(hostname=self.local_host)
self.job_cache = []
# Setup zeroconf
self.zeroconf = ZeroconfServer("_zordon._tcp.local.", socket.gethostname(), 8080)
self.zeroconf.start(listen_only=True)
# Setup photo preview
photo_pad = tk.Frame(self.root, background="gray")
photo_pad.pack(fill=tk.BOTH, pady=5, padx=5)
@@ -129,7 +133,7 @@ class ZordonClient:
pass
# update servers
self.populate_server_tree()
self.update_servers()
try:
selected_server = self.server_tree.get_children()[0]
self.server_tree.selection_set(selected_server)
@@ -141,15 +145,12 @@ class ZordonClient:
x.daemon = True
x.start()
def populate_server_tree(self):
servers = available_servers()
self.server_tree.delete(*self.server_tree.get_children())
for hostname in servers:
self.server_tree.insert("", tk.END, iid=hostname, values=(hostname,))
def server_picked(self, event):
try:
new_hostname = self.server_tree.selection()[0]
self.server_proxy.hostname = new_hostname
except IndexError:
pass
self.job_cache.clear()
self.update_jobs(clear_table=True)
@@ -218,10 +219,20 @@ class ZordonClient:
def __background_update(self):
while True:
self.update_jobs()
time.sleep(1)
self.update_servers()
time.sleep(3)
def update_servers(self):
servers = self.zeroconf.found_clients()
if len(servers) < len(self.server_tree.get_children()):
self.server_tree.delete(*self.server_tree.get_children())
for hostname in servers:
if hostname not in self.server_tree.get_children():
self.server_tree.insert("", tk.END, iid=hostname, values=(hostname,))
def update_jobs(self, clear_table=False):
def update_jobs_inner():
def update_row(tree, id, new_values, tags=None):
for item in tree.get_children():
values = tree.item(item, "values")
@@ -231,10 +242,8 @@ class ZordonClient:
job_fetch = self.server_proxy.get_jobs()
if job_fetch is not None:
if len(job_fetch) < len(self.job_cache) or len(job_fetch) == 0:
clear_table = True
self.job_cache = job_fetch # update the cache only if its good data
if clear_table:
self.job_tree.delete(*self.job_tree.get_children())
self.job_cache = job_fetch # update the cache only if its good data
for job in self.job_cache:
display_status = job['status'] if job['status'] != 'running' else \
('%.0f%%' % (job['percent_complete'] * 100)) # if running, show percentage, otherwise just show status
@@ -246,10 +255,20 @@ class ZordonClient:
display_status,
job['time_elapsed'],
job['total_frames'])
try:
if self.job_tree.exists(job['id']):
update_row(self.job_tree, job['id'], new_values=values, tags=tags)
else:
self.job_tree.insert("", tk.END, iid=job['id'], values=values, tags=tags)
except tk.TclError:
pass
if clear_table:
self.job_tree.delete(*self.job_tree.get_children())
x = threading.Thread(target=update_jobs_inner)
x.daemon = True
x.start()
def show_new_job_window(self):
new_window = tk.Toplevel(self.root)

View File

@@ -11,7 +11,8 @@ from tkinter.ttk import Frame, Label, Entry, Combobox
import psutil
import requests
import sys
sys.path.append('../../')
from lib.render_workers.blender_worker import Blender
from server_proxy import RenderServerProxy

View File

@@ -34,15 +34,16 @@ class RenderServerProxy:
def request(self, payload, timeout=5):
return requests.get(f'http://{self.hostname}:{self.port}/api/{payload}', timeout=timeout)
def get_jobs(self):
all_jobs = self.request_data('jobs')
def get_jobs(self, timeout=5):
all_jobs = self.request_data('jobs', timeout=timeout)
if all_jobs is not None:
sorted_jobs = []
if all_jobs:
for status_category in categories:
found_jobs = [x for x in all_jobs if x['status'] == status_category.value]
if found_jobs:
sorted_jobs.extend(found_jobs)
return sorted_jobs
all_jobs = sorted_jobs
return all_jobs
def get_data(self, timeout=5):
all_data = self.request_data('full_status', timeout=timeout)

View File

@@ -16,6 +16,7 @@ import yaml
from flask import Flask, request, render_template, send_file, after_this_request, Response, redirect, url_for, abort
from werkzeug.utils import secure_filename
from lib.server.zeroconf_server import ZeroconfServer
from lib.render_queue import RenderQueue, JobNotFoundError
from lib.render_workers.worker_factory import RenderWorkerFactory
from lib.render_workers.base_worker import string_to_status, RenderStatus
@@ -504,6 +505,9 @@ def start_server(background_thread=False):
logging.info(f"Starting Zordon Render Server - Hostname: '{RenderQueue.hostname}'")
zeroconf_server = ZeroconfServer("_zordon._tcp.local.", RenderQueue.hostname, RenderQueue.port)
zeroconf_server.start()
if background_thread:
server_thread = threading.Thread(
target=lambda: server.run(host='0.0.0.0', port=RenderQueue.port, debug=False, use_reloader=False))

View File

@@ -0,0 +1,73 @@
import logging
import socket
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser, ServiceStateChange
logger = logging.getLogger()
class ZeroconfServer():
def __init__(self, service_type, server_name, server_port):
self.service_type = service_type
self.server_name = server_name
self.server_port = server_port
self.server_ip = None
self.zeroconf = Zeroconf()
self.service_info = None
self.client_cache = {}
self.properties = {}
def start(self, listen_only=False):
if not listen_only:
self._register_service()
self._browse_services()
def stop(self):
self._unregister_service()
self.zeroconf.close()
def _register_service(self):
self.server_ip = socket.gethostbyname(socket.gethostname())
info = ServiceInfo(
self.service_type,
f"{self.server_name}.{self.service_type}",
addresses=[socket.inet_aton(self.server_ip)],
port=self.server_port,
properties=self.properties,
)
self.service_info = info
self.zeroconf.register_service(info)
logger.info(f"Registered zeroconf service: {self.service_info.name}")
def _unregister_service(self):
if self.service_info:
self.zeroconf.unregister_service(self.service_info)
logger.info(f"Unregistered zeroconf service: {self.service_info.name}")
self.service_info = None
def _browse_services(self):
browser = ServiceBrowser(self.zeroconf, self.service_type, [self._on_service_discovered])
def _on_service_discovered(self, zeroconf, service_type, name, state_change):
info = zeroconf.get_service_info(service_type, name)
logger.debug(f"Zeroconf: {name} {state_change}")
if service_type == self.service_type:
if state_change == ServiceStateChange.Added or state_change == ServiceStateChange.Updated:
self.client_cache[name] = info
else:
self.client_cache.pop(name)
def found_clients(self):
return [x.split(f'.{self.service_type}')[0] for x in self.client_cache.keys()]
# Example usage:
if __name__ == "__main__":
server = ZeroconfServer("_zordon._tcp.local.", "foobar.local", 8080)
try:
server.start()
input("Server running - Press enter to end")
finally:
server.stop()

View File

@@ -11,3 +11,4 @@ future==0.18.3
json2html~=1.3.0
SQLAlchemy~=2.0.15
Pillow~=9.3.0
zeroconf~=0.63.0