mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 16:58:12 +00:00
Zeroconf reports system properties (#59)
* Zeroconf.found_clients() now returns dicts of clients, not just hostnames * Adjustments to distributed_job_manager.py * Undo config change * Report system metrics (cpu, os, etc) via zeroconf_server.py * Zeroconf.found_clients() now returns dicts of clients, not just hostnames * Adjustments to distributed_job_manager.py * Undo config change * Zeroconf.found_clients() now returns dicts of clients, not just hostnames * Adjustments to distributed_job_manager.py * Undo config change * Adjustments to distributed_job_manager.py * Undo config change * Rename ZeroconfServer.found_clients() to found_hostnames()
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 249 B After Width: | Height: | Size: 450 B |
BIN
resources/icons/linux.png
Normal file
BIN
resources/icons/linux.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/icons/macos.png
Normal file
BIN
resources/icons/macos.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/icons/windows.png
Normal file
BIN
resources/icons/windows.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 806 B |
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
@@ -277,7 +278,7 @@ def snapshot():
|
|||||||
@server.get('/api/_detected_clients')
|
@server.get('/api/_detected_clients')
|
||||||
def detected_clients():
|
def detected_clients():
|
||||||
# todo: dev/debug only. Should not ship this - probably.
|
# todo: dev/debug only. Should not ship this - probably.
|
||||||
return ZeroconfServer.found_clients()
|
return ZeroconfServer.found_hostnames()
|
||||||
|
|
||||||
|
|
||||||
# New version
|
# New version
|
||||||
@@ -548,6 +549,9 @@ def start_server():
|
|||||||
|
|
||||||
logger.info(f"Starting Zordon Render Server - Hostname: '{server.config['HOSTNAME']}:'")
|
logger.info(f"Starting Zordon Render Server - Hostname: '{server.config['HOSTNAME']}:'")
|
||||||
ZeroconfServer.configure("_zordon._tcp.local.", server.config['HOSTNAME'], server.config['PORT'])
|
ZeroconfServer.configure("_zordon._tcp.local.", server.config['HOSTNAME'], server.config['PORT'])
|
||||||
|
ZeroconfServer.properties = {'system_cpu': current_system_cpu(), 'system_cpu_cores': multiprocessing.cpu_count(),
|
||||||
|
'system_os': current_system_os(),
|
||||||
|
'system_os_version': current_system_os_version()}
|
||||||
ZeroconfServer.start()
|
ZeroconfServer.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -220,10 +220,10 @@ class DistributedJobManager:
|
|||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def split_into_subjobs(cls, worker, job_data, project_path):
|
def split_into_subjobs(cls, worker, job_data, project_path, system_os=None):
|
||||||
|
|
||||||
# Check availability
|
# Check availability
|
||||||
available_servers = cls.find_available_servers(worker.renderer)
|
available_servers = cls.find_available_servers(worker.renderer, system_os)
|
||||||
logger.debug(f"Splitting into subjobs - Available servers: {available_servers}")
|
logger.debug(f"Splitting into subjobs - Available servers: {available_servers}")
|
||||||
subjob_servers = cls.distribute_server_work(worker.start_frame, worker.end_frame, available_servers)
|
subjob_servers = cls.distribute_server_work(worker.start_frame, worker.end_frame, available_servers)
|
||||||
local_hostname = socket.gethostname()
|
local_hostname = socket.gethostname()
|
||||||
@@ -353,17 +353,20 @@ class DistributedJobManager:
|
|||||||
return server_breakdown
|
return server_breakdown
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_available_servers(engine_name):
|
def find_available_servers(engine_name, system_os=None):
|
||||||
"""
|
"""
|
||||||
Scan the Zeroconf network for currently available render servers supporting a specific engine.
|
Scan the Zeroconf network for currently available render servers supporting a specific engine.
|
||||||
|
|
||||||
:param engine_name: str, The engine type to search for
|
:param engine_name: str, The engine type to search for
|
||||||
|
:param system_os: str, Restrict results to servers running a specific OS
|
||||||
:return: A list of dictionaries with each dict containing hostname and cpu_count of available servers
|
:return: A list of dictionaries with each dict containing hostname and cpu_count of available servers
|
||||||
"""
|
"""
|
||||||
available_servers = []
|
available_servers = []
|
||||||
for hostname in ZeroconfServer.found_clients():
|
for hostname in ZeroconfServer.found_hostnames():
|
||||||
response = RenderServerProxy(hostname).is_engine_available(engine_name)
|
host_properties = ZeroconfServer.get_hostname_properties(hostname)
|
||||||
if response and response.get('available', False):
|
if not system_os or (system_os and system_os == host_properties.get('system_os')):
|
||||||
available_servers.append(response)
|
response = RenderServerProxy(hostname).is_engine_available(engine_name)
|
||||||
|
if response and response.get('available', False):
|
||||||
|
available_servers.append(response)
|
||||||
|
|
||||||
return available_servers
|
return available_servers
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ class NewRenderJobForm(QWidget):
|
|||||||
self.renderer_type.addItems(self.renderer_info.keys())
|
self.renderer_type.addItems(self.renderer_info.keys())
|
||||||
|
|
||||||
def update_server_list(self):
|
def update_server_list(self):
|
||||||
clients = ZeroconfServer.found_clients()
|
clients = ZeroconfServer.found_hostnames()
|
||||||
self.server_input.clear()
|
self.server_input.clear()
|
||||||
self.server_input.addItems(clients)
|
self.server_input.addItems(clients)
|
||||||
|
|
||||||
|
|||||||
@@ -214,25 +214,14 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# Update the Server Info box when a server is changed
|
# Update the Server Info box when a server is changed
|
||||||
self.server_info_hostname.setText(self.current_hostname or "unknown")
|
self.server_info_hostname.setText(self.current_hostname or "unknown")
|
||||||
if self.current_server_proxy.system_os:
|
server_info = ZeroconfServer.get_hostname_properties(self.current_hostname)
|
||||||
self.server_info_os.setText(f"OS: {self.current_server_proxy.system_os} "
|
if server_info:
|
||||||
f"{self.current_server_proxy.system_os_version}")
|
self.server_info_os.setText(f"OS: {server_info['system_os']} {server_info['system_os_version']}")
|
||||||
self.server_info_cpu.setText(f"CPU: {self.current_server_proxy.system_cpu} - "
|
self.server_info_cpu.setText(f"CPU: {server_info['system_cpu']} - {server_info.get('system_cpu_cores')} cores")
|
||||||
f"{self.current_server_proxy.system_cpu_count} cores")
|
|
||||||
else:
|
else:
|
||||||
self.server_info_os.setText(f"OS: Loading...")
|
self.server_info_os.setText(f"OS: Unknown")
|
||||||
self.server_info_cpu.setText(f"CPU: Loading...")
|
self.server_info_cpu.setText(f"CPU: Unknown")
|
||||||
|
|
||||||
def update_server_info_worker():
|
|
||||||
server_details = self.current_server_proxy.get_status()
|
|
||||||
if server_details['hostname'] == self.current_hostname:
|
|
||||||
self.server_info_os.setText(f"OS: {server_details.get('system_os')} "
|
|
||||||
f"{server_details.get('system_os_version')}")
|
|
||||||
self.server_info_cpu.setText(f"CPU: {server_details.get('system_cpu')} - "
|
|
||||||
f"{server_details.get('cpu_count')} cores")
|
|
||||||
|
|
||||||
update_thread = threading.Thread(target=update_server_info_worker)
|
|
||||||
update_thread.start()
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -379,7 +368,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.image_label.setPixmap(pixmap)
|
self.image_label.setPixmap(pixmap)
|
||||||
|
|
||||||
def update_servers(self):
|
def update_servers(self):
|
||||||
found_servers = list(set(ZeroconfServer.found_clients() + self.added_hostnames))
|
found_servers = list(set(ZeroconfServer.found_hostnames() + self.added_hostnames))
|
||||||
# Always make sure local hostname is first
|
# Always make sure local hostname is first
|
||||||
current_hostname = socket.gethostname()
|
current_hostname = socket.gethostname()
|
||||||
if found_servers and found_servers[0] != current_hostname:
|
if found_servers and found_servers[0] != current_hostname:
|
||||||
@@ -402,7 +391,8 @@ class MainWindow(QMainWindow):
|
|||||||
current_server_list.append(self.server_list_view.item(i).text())
|
current_server_list.append(self.server_list_view.item(i).text())
|
||||||
for hostname in found_servers:
|
for hostname in found_servers:
|
||||||
if hostname not in current_server_list:
|
if hostname not in current_server_list:
|
||||||
image_path = os.path.join(resources_dir(), 'icons', 'Monitor.png')
|
properties = ZeroconfServer.get_hostname_properties(hostname)
|
||||||
|
image_path = os.path.join(resources_dir(), 'icons', f"{properties.get('system_os', 'Monitor')}.png")
|
||||||
list_widget = QListWidgetItem(QIcon(image_path), hostname)
|
list_widget = QListWidgetItem(QIcon(image_path), hostname)
|
||||||
self.server_list_view.addItem(list_widget)
|
self.server_list_view.addItem(list_widget)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
import zeroconf
|
||||||
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser, ServiceStateChange
|
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser, ServiceStateChange
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -24,6 +25,8 @@ class ZeroconfServer:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def start(cls, listen_only=False):
|
def start(cls, listen_only=False):
|
||||||
|
if not cls.service_type:
|
||||||
|
raise RuntimeError("The 'configure' method must be run before starting the zeroconf server")
|
||||||
if not listen_only:
|
if not listen_only:
|
||||||
cls._register_service()
|
cls._register_service()
|
||||||
cls._browse_services()
|
cls._browse_services()
|
||||||
@@ -49,8 +52,8 @@ class ZeroconfServer:
|
|||||||
cls.service_info = info
|
cls.service_info = info
|
||||||
cls.zeroconf.register_service(info)
|
cls.zeroconf.register_service(info)
|
||||||
logger.info(f"Registered zeroconf service: {cls.service_info.name}")
|
logger.info(f"Registered zeroconf service: {cls.service_info.name}")
|
||||||
except socket.gaierror as e:
|
except zeroconf.NonUniqueNameException as e:
|
||||||
logger.error(f"Error starting zeroconf service: {e}")
|
logger.error(f"Error establishing zeroconf: {e}")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _unregister_service(cls):
|
def _unregister_service(cls):
|
||||||
@@ -75,19 +78,24 @@ class ZeroconfServer:
|
|||||||
cls.client_cache.pop(name)
|
cls.client_cache.pop(name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def found_clients(cls):
|
def found_hostnames(cls):
|
||||||
|
|
||||||
fetched_hostnames = [x.split(f'.{cls.service_type}')[0] for x in cls.client_cache.keys()]
|
fetched_hostnames = [x.split(f'.{cls.service_type}')[0] for x in cls.client_cache.keys()]
|
||||||
local_hostname = socket.gethostname()
|
local_hostname = socket.gethostname()
|
||||||
# Define a sort key function
|
# Define a sort key function
|
||||||
def sort_key(hostname):
|
def sort_key(hostname):
|
||||||
# Return 0 if it's the local hostname so it comes first, else return 1
|
# Return 0 if it's the local hostname so it comes first, else return 1
|
||||||
return 0 if hostname == local_hostname else 1
|
return False if hostname == local_hostname else True
|
||||||
|
|
||||||
# Sort the list with the local hostname first
|
# Sort the list with the local hostname first
|
||||||
sorted_hostnames = sorted(fetched_hostnames, key=sort_key)
|
sorted_hostnames = sorted(fetched_hostnames, key=sort_key)
|
||||||
return sorted_hostnames
|
return sorted_hostnames
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_hostname_properties(cls, hostname):
|
||||||
|
new_key = hostname + '.' + cls.service_type
|
||||||
|
server_info = cls.client_cache.get(new_key).properties
|
||||||
|
decoded_server_info = {key.decode('utf-8'): value.decode('utf-8') for key, value in server_info.items()}
|
||||||
|
return decoded_server_info
|
||||||
|
|
||||||
# Example usage:
|
# Example usage:
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user