185 lines
7.0 KiB
Python
185 lines
7.0 KiB
Python
|
import socket
|
||
|
import time
|
||
|
import xml.etree.ElementTree as ET
|
||
|
import threading
|
||
|
import json
|
||
|
import cherrypy
|
||
|
|
||
|
# Configuration
|
||
|
SYSTEM_MANAGER_HOST = "sysman.freeradionetwork.eu"
|
||
|
SYSTEM_MANAGER_PORT = 10025
|
||
|
|
||
|
# Global data storage
|
||
|
data_lock = threading.Lock()
|
||
|
fetched_data = {
|
||
|
'servers': []
|
||
|
}
|
||
|
|
||
|
def parse_client_info(client_info_str):
|
||
|
"""
|
||
|
Parses the client info string and returns a dictionary of fields.
|
||
|
"""
|
||
|
# Wrap the string with a root tag to make it well-formed XML
|
||
|
xml_str = f"<root>{client_info_str}</root>"
|
||
|
client_info = {}
|
||
|
|
||
|
try:
|
||
|
root = ET.fromstring(xml_str)
|
||
|
# Mapping of tag names to field names
|
||
|
tags = {
|
||
|
'ON': 'CallsignAndUser',
|
||
|
'BC': 'BandAndChannel',
|
||
|
'DS': 'Description',
|
||
|
'NN': 'Country',
|
||
|
'CT': 'CityCityPart'
|
||
|
}
|
||
|
for tag, field in tags.items():
|
||
|
element = root.find(tag)
|
||
|
if element is not None and element.text is not None:
|
||
|
client_info[field] = element.text.strip()
|
||
|
else:
|
||
|
client_info[field] = None
|
||
|
except ET.ParseError as e:
|
||
|
print(f"Error parsing client info XML: {e}")
|
||
|
print(f"Client info string: {client_info_str}")
|
||
|
return client_info
|
||
|
|
||
|
def fetch_system_explorer_info():
|
||
|
global fetched_data
|
||
|
while True:
|
||
|
try:
|
||
|
temp_data = {'servers': []}
|
||
|
# Connect to the SystemManager
|
||
|
with socket.create_connection((SYSTEM_MANAGER_HOST, SYSTEM_MANAGER_PORT)) as sock:
|
||
|
# Send the 'SM' command with CRLF (\r\n)
|
||
|
sock.sendall(b'SM\r\n')
|
||
|
|
||
|
# Wrap the socket into a file-like object for easier reading
|
||
|
sock_file = sock.makefile('r', encoding='utf-8', errors='replace')
|
||
|
|
||
|
# Receive and interpret server count
|
||
|
server_count_line = sock_file.readline()
|
||
|
server_count_line = server_count_line.strip()
|
||
|
|
||
|
if not server_count_line.isdigit():
|
||
|
print("Unexpected response:", server_count_line)
|
||
|
continue
|
||
|
|
||
|
server_count = int(server_count_line)
|
||
|
|
||
|
# Loop through each server based on the count received
|
||
|
for server_index in range(server_count):
|
||
|
# Read server information
|
||
|
server_name_line = sock_file.readline()
|
||
|
if not server_name_line:
|
||
|
print("No more data from server when expecting server name.")
|
||
|
break
|
||
|
server_name = server_name_line.strip()
|
||
|
|
||
|
server_entry = {
|
||
|
'server_name': server_name,
|
||
|
'nets': []
|
||
|
}
|
||
|
|
||
|
# Read net count for this server
|
||
|
net_count_line = sock_file.readline()
|
||
|
if not net_count_line:
|
||
|
print(f"No more data from server when expecting net count for server '{server_name}'.")
|
||
|
break
|
||
|
net_count_line = net_count_line.strip()
|
||
|
try:
|
||
|
net_count = int(net_count_line)
|
||
|
except ValueError:
|
||
|
print(f"Error parsing net count: {net_count_line}")
|
||
|
continue
|
||
|
|
||
|
# Loop through each net
|
||
|
for net_index in range(net_count):
|
||
|
# Read net information
|
||
|
net_name_line = sock_file.readline()
|
||
|
if not net_name_line:
|
||
|
print(f"No more data from server when expecting net name for server '{server_name}'.")
|
||
|
break
|
||
|
net_name = net_name_line.strip()
|
||
|
|
||
|
net_entry = {
|
||
|
'net_name': net_name,
|
||
|
'clients': []
|
||
|
}
|
||
|
|
||
|
# Read client count for this net
|
||
|
client_count_line = sock_file.readline()
|
||
|
if not client_count_line:
|
||
|
print(f"No more data from server when expecting client count for net '{net_name}'.")
|
||
|
break
|
||
|
client_count_line = client_count_line.strip()
|
||
|
try:
|
||
|
client_count = int(client_count_line)
|
||
|
except ValueError:
|
||
|
print(f"Error parsing client count: {client_count_line}")
|
||
|
continue
|
||
|
|
||
|
# Loop through each client
|
||
|
for client_index in range(client_count):
|
||
|
client_info_line = sock_file.readline()
|
||
|
if not client_info_line:
|
||
|
print(f"No more data from server when expecting client info for net '{net_name}'.")
|
||
|
break
|
||
|
client_info_line = client_info_line.strip()
|
||
|
# Parse the client info string
|
||
|
client_info = parse_client_info(client_info_line)
|
||
|
net_entry['clients'].append(client_info)
|
||
|
server_entry['nets'].append(net_entry)
|
||
|
temp_data['servers'].append(server_entry)
|
||
|
|
||
|
# Update the global data
|
||
|
with data_lock:
|
||
|
fetched_data = temp_data
|
||
|
|
||
|
print("Data fetching complete. Sleeping for 60 seconds...")
|
||
|
time.sleep(60) # Fetch data every minute
|
||
|
|
||
|
except Exception as e:
|
||
|
print(f"Error: {e}")
|
||
|
time.sleep(60) # Wait before retrying
|
||
|
|
||
|
class FRNAPI:
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_out()
|
||
|
def index(self):
|
||
|
with data_lock:
|
||
|
return fetched_data
|
||
|
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_out()
|
||
|
def servers(self):
|
||
|
with data_lock:
|
||
|
return fetched_data.get('servers', [])
|
||
|
|
||
|
@cherrypy.expose
|
||
|
@cherrypy.tools.json_out()
|
||
|
def server(self, server_name=None):
|
||
|
if not server_name:
|
||
|
return {'error': 'server_name parameter is required'}
|
||
|
with data_lock:
|
||
|
for server in fetched_data.get('servers', []):
|
||
|
if server['server_name'] == server_name:
|
||
|
return server
|
||
|
return {'error': f'Server "{server_name}" not found'}
|
||
|
|
||
|
# You can add more endpoints here as needed
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
# Start the data fetching thread
|
||
|
data_thread = threading.Thread(target=fetch_system_explorer_info, daemon=True)
|
||
|
data_thread.start()
|
||
|
|
||
|
# Configure CherryPy
|
||
|
cherrypy.config.update({
|
||
|
'server.socket_host': '0.0.0.0', # Listen on all interfaces
|
||
|
'server.socket_port': 8080, # Change the port if needed
|
||
|
})
|
||
|
|
||
|
# Start the CherryPy web server
|
||
|
cherrypy.quickstart(FRNAPI())
|